【技术实操】银河高级服务器操作系统实例分享,TCP长连接与短连接详细说明

1.服务器环境以及配置

物理机/虚拟机/云/容器

物理机

处理器:

HUAWEI Kunpeng 920

具体操作系统版本

Kylin-Server-10-SP1-Release-Build20-20210518- aarch64

内核版本

kernel-4.19.90-23.8.v2101.ky10.aarch64

2.问题现象描述

对TCP长连接有疑问

1、如何从命令查看及确认哪些是长连接?

2、netstat命令查看结果中timers字段中第一列四种状态的具体含义

  1. keepalive #表示是keepalive的时间计时(希望可对照图 1-1具体是出现在哪个环节,从哪里开始计时?)
  2. on #表示是重发(retransmission)的时间计时(希望可对照图 1-1说明出现在哪个环节?)
  3. off #表示没有时间计时(希望可对照图 1-1说明出现在哪个环节?)
  4. timewait #表示等待(timewait)时间计时(希望可对照图 1-1说明出现在哪个环节?)

3、netstat命令查看结果中timers字段中第二列三个数据的具体含义状态的具体含义

例如:keepalive (576.47/1/1)

3.问题分析

3.1.长连接与短连接

长连接(long connnection),指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包。

短连接(short connnection),是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时才去建立一个连接,数据发送完成后则断开此连接,即每次连接只完成一项业务的发送。

区分长连接和短连接主要看以下几点:

  • 连接持续时间:长连接通常持续较长时间,而短连接持续时间较短。通常情况下,长连接可以持续数分钟、数小时甚至更长时间,而短连接通常只持续几秒钟或几分钟。
  • 连接的目的:长连接通常用于需要保持持久连接的应用程序,例如在线聊天、实时通信、视频流传输等。短连接通常用于一次性数据传输,如HTTP/1.0请求和响应。
  • 连接的频率:长连接通常会在一段时间内持续传输数据,而短连接通常是一次性的数据传输。如果您看到多次数据传输在同一个连接上进行,那么可能是长连接。如果每个数据传输都伴随着新的连接建立和关闭,那么可能是短连接。
  • 连接状态:使用网络监控工具如netstat或ss,可以查看当前的TCP连接状态。长连接通常处于ESTABLISHED状态,而短连接可能会更快地进入CLOSED状态。
  • 应用程序协议:有些应用程序协议明确规定了连接的类型。例如,HTTP/1.0通常使用短连接,而HTTP/1.1支持持久连接。

3.2.如何从命令查看及确认哪些是长连接

3.2.1 使用tcpdump或者wireshark抓包查看

以HTTP1.1为例的长连接。

HTTP 1.1默认采用Keep-Alive连接,允许一个HTTP连接上的多个请求/响应交换,而不需要为每个请求重新建立一个TCP连接。短连接是指每次请求/响应完成后就关闭TCP连接,下次请求需要重新建立TCP连接。相比之下,Keep-Alive连接在一定时间内可以重复使用,提高了效率和性能。当然,如果需要,客户端和服务器都可以通过相应的配置或请求头来使用短连接。但是,随着Web应用的复杂度增加,使用Keep-Alive连接成为了一种更常见的做法。

从抓包可见HTTP1.1的Keep-Alive设置的timeout为60s。从654号包,开始计时,如果超时时间不更新的话,就会在60s之后断开连接,在654和2794之间没有timeout的更新,所以连接断开了。

3.2.2 使用netstat和ss查看

netstat -tonp,查看tcp状态和timer。

可以查看当前的TCP连接状态。长连接通常处于ESTABLISHED状态,而短连接可能会更快地进入CLOSED状态。长连接一般会使用心跳机制进行探测。

3.3 netstat命令查看结果中timers字段中第一列四种状态的具体含义

3.3.1 keepalive

keepalive:keepalive计时器通常在ESTABLISHED状态下使用。当TCP连接处于ESTABLISHED状态时,系统可以启动keepalive计时器来定期发送keepalive探测包,以检测连接的健康状态。

3.3.2 on

on:重发计时器。

建立连接时:当TCP连接刚刚建立时,RTO的计时器通常不会立即启动,因为还没有数据包被发送。连接建立后,RTO会在数据包被发送后开始计时。

数据包发送:RTO与每个已发送但未确认的数据包相关联。每当发送方发送一个数据包,RTO就会启动并开始计时,等待接收方的确认。

等待确认:RTO在等待接收方确认数据包的过程中运行。如果在RTO超时之前未收到确认,发送方将触发数据包的重传。

传输结束:RTO也可以在TCP连接关闭时出现。如果有未确认的数据包仍然存在于连接中,RTO会继续运行,以确保这些数据包得到确认或重传。

3.3.3 timewait

timewait:timewait计时器在TIME_WAIT状态下使用。在连接关闭后,连接的一方会进入TIME_WAIT状态,timewait计时器用于等待一段时间,以确保在此期间不会出现重复的连接请求。

3.3.4 off

off:没有时间计时,例如ESTABLISHED没有特殊的计时器操作,应用程序设置关闭了keepalive。如果State列为CLOSE_WAIT状态是,Timer列多为off (0.00/0/0),因为CLOSE_WAIT的是属于被动关闭那一方,这个是没有超时(timeout)设置的,所以也就不用计时了。CLOSE_WAIT除非你杀进程,CLOSE_WAIT是不会自动消失的。一个CLOSE_WAIT会维持至少2个小时的时间。当然不消失意味着占着资源呢,这里就是占着FD。

例如:应用服务端设置关闭KeepAlive

1、准备两台虚拟机

server 192.168.55.52
clinet 192.168.55.12

2、准备服务端程序

服务端监听1234端口。

ssh root@192.168.55.52
vim tcp_server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>

#define MAX_EVENTS 10
#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("请提供要监控的端口号作为参数\n");
        return 1;
    }

    int port = atoi(argv[1]);

    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("无法创建套接字");
        return 1;
    }
    int keepAlive=0;//设置关闭keepAlive
    if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) < 0) {
        perror("设置失败");
        return 1;
    }

    // 绑定地址和端口
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("无法绑定地址和端口");
        return 1;
    }

    // 监听连接
    if (listen(sockfd, 10) < 0) {
        perror("无法监听连接");
        return 1;
    }

    printf("正在监听端口 %d ...\n", port);

    // 创建 epoll 实例
    int epollfd = epoll_create1(0);
    if (epollfd < 0) {
        perror("无法创建 epoll 实例");
        return 1;
    }

    // 添加 sockfd 到 epoll 实例中进行监听
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) < 0) {
        perror("无法添加 sockfd 到 epoll 实例");
        return 1;
    }

    // 准备接收连接的事件
    struct epoll_event events[MAX_EVENTS];

    while (1) {
        // 等待事件发生
        int num_events = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (num_events < 0) {
            perror("epoll_wait 出错");
            return 1;
        }

    int i=0;
        // 处理所有事件
        for (i = 0; i < num_events; i++) {
            if (events[i].data.fd == sockfd) {
                // 有新连接请求
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_addr);
                int client_sockfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
                if (client_sockfd < 0) {
                    perror("无法接受连接");
                    return 1;
                }

                // 将新连接的套接字添加到 epoll 实例中进行监听
                event.events = EPOLLIN;
                event.data.fd = client_sockfd;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sockfd, &event) < 0) {
                    perror("无法添加新连接的套接字到 epoll 实例");
                    return 1;
                }

                // 获取客户端信息
                char client_ip[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
                int client_port = ntohs(client_addr.sin_port);
                printf("已连接客户端:%s:%d\n", client_ip, client_port);
            } else {
                // 有数据可读
                int client_sockfd = events[i].data.fd;
                char buffer[BUFFER_SIZE];
                ssize_t recv_len = recv(client_sockfd, buffer, BUFFER_SIZE - 1, 0);
                if (recv_len > 0) {
                    buffer[recv_len] = '\0';
                    printf("接收到的字符串:%s\n", buffer);
                } else if (recv_len == 0) {
                    // 客户
                // 从 epoll 实例中移除套接字
                if (epoll_ctl(epollfd, EPOLL_CTL_DEL, client_sockfd, NULL) < 0) {
                    perror("无法从 epoll 实例中移除套接字");
                    return 1;
                }

                // 关闭套接字
                close(client_sockfd);
            } else {
                perror("接收数据出错");
                return 1;
            }
        }
    }
}

// 关闭套接字和 epoll 实例
close(sockfd);
close(epollfd);

return 0;
}

# 编译
gcc tcp_server.c -o server

./server 1234

3、准备客户端程序

客户端绑定54321端口

ssh root@192.168.55.12
vim tcp_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 54321

int main(int argc, char *argv[]) {
    int sock; // socket
    struct sockaddr_in server_addr; // server address
    char *server_ip; // server IP address
    int server_port; // server port

    // Check command line arguments
    if (argc != 3) {
        printf("Usage: %s <server_ip> <server_port>\n", argv[0]);
        return 1;
    }

    // Get server IP address and port
    server_ip = argv[1];
    server_port = atoi(argv[2]);

    // Create socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket failed");
        return 1;
    }

    // Set address and port of server
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(server_ip);
    server_addr.sin_port = htons(server_port);

    // Bind client socket to port 54321
    struct sockaddr_in client_addr = {
        .sin_family = AF_INET,
        .sin_addr.s_addr = INADDR_ANY,
        .sin_port = htons(PORT)
    };

    if (bind(sock, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) {
        perror("bind failed");
        return 2;
    }

    // Connect to server
    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect failed");
        return 1;
    }

    // Print success message
    printf("Connected to server %s:%d\n", server_ip, server_port);
    send(sock,"hello",6,0);
    while(1);
    // Close socket
    close(sock);

    return 0;
}

gcc tcp_client.c -o client

netstat -W -neoap查看服务端,后面的off表示关闭了timer,若服务端不关闭keepalive,使用netstat 查看到的timer应是keepalive。

3.4 netstat命令查看结果中timers字段第二列三个数据的具体含义状态的具体含义

第二列,(576.47/0/0) -> (a/b/c)

3.3.1 当第一列为keepalive计时器

a

定时器计时值的倒计时,单位秒;刚建立连接时为最大值。

如果系统没有特殊设置或者应用程序没有设置keepalive,起始值是/proc/sys/net/ipv4/tcp_keepalive_time默认值7200秒,倒计时直到值变为0。

b

没有用

c

已发送的keepalive探测器的数量(已经发送的探测(probe)包的次数)。默认为0;最大值和tcp_keepalive_probes有关系,/proc/sys/net/ipv4/tcp_keepalive_probes代表总共发送探测(probe)包的个数(默认为9个)。

当a值倒计时为0,而连接中间没有数据传输,这个值将会累加。

而/proc/sys/net/ipv4/tcp_keepalive_intvl表示在发送一个探测(probe)包后,如果多少秒内没有收到回复,则再发送一个探测(probe)包。这也代表了之前发送的探测(probe)包超时失效(默认为75秒)。当所有的探测(probe)包都发送完毕后,如果仍然没有收到回应,服务器会主动关闭该连接(长连接)。

因此,通常情况下,如果第二列的c为0,a的范围应在7200到0之间,其中7200是/proc/sys/net/ipv4/tcp_keepalive_time的值,例如keepalive (576.47/0/0) ;如果c不为0,但不可能大于/proc/sys/net/ipv4/tcp_keepalive_probes的值,那么a的范围应在75到0之间,其中75是tcp_keepalive_intvl的值,例如keepalive (73.06/0/2)。

3.3.2 当第一列为on计时器

a

重发(retransmission)倒计时,单位秒

b

已经产生的重发(retransmission)次数

c

没有使用

3.3.3 当第一列为timewait计时器

a

TIMEWAIT状态倒计时值,单位秒,起始值为60(两倍MSL的时间值)。当时间倒计时为0,连接就会由TIMEWAIT状态就会变为CLOSE状态

b

没有使用

c

没有使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/15880.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

兴业证券 | 哪些行业在提价?

一方面&#xff0c; 部分行业年初以来PPI价格整体上涨&#xff0c;4月进一步提价&#xff1b;另一方面&#xff0c;部分行业年初以来PPI价格整体下跌或者涨幅不高&#xff0c;但4月开始出现边际提升。 前言&#xff1a;年初以来&#xff0c;“提价”是一条重要的投资线索。我们…

Unity Assembly Definition Dotween 引用

原理&#xff1a; 具体Unity程序集原理用法&#xff0c;暂时留坑&#xff0c;不介绍了&#xff0c;相信有很多人也写过了 这里简单放个官方API链接 https://docs.unity3d.com/cn/current/Manual/ScriptCompilationAssemblyDefinitionFiles.html 现象 &#xff1a;Dotween引用…

使OpenCV可以读取中文路径图片的方法

一&#xff0e;问题复现 1.代码 #! /usr/bin/env python # -*- coding: utf-8 -*-# File: show_img.pyimport cv2# 读取图片 img cv2.imread("车牌素材/冀A.png")# 显示图片 cv2.imshow("img", img) cv2.waitKey(0)2.报错截图 3.报错内容 [ WARN:00.05…

ISCC——AI

得到一个T4.pyc 回编译一下 得到下面代码 import base64def encrypt_and_compare(user_input, offset_str, target_base64):if len(user_input) ! 24:return Please enter a string with a length of 24encrypted Nonefor i, char in enumerate(user_input):offset int(off…

Unity开发——XLua热更新之Hotfix配置(包含xlua获取与导入)

一、Git上获取xlua 最新的xlua包&#xff0c;下载地址链接&#xff1a;https://github.com/Tencent/xLua 二、Unity添加xlua 解压xlua压缩包后&#xff0c;将xlua里的Assets里的文件直接复制进Unity的Assets文件夹下。 成功导入后&#xff0c;unity工具栏会出现xlua选项。 …

到底该用英文括号还是中文括号?

这篇博客写的还挺详细的&#xff0c;不错。

TCP/IP协议族

基于这张图片的一篇blog TCP/IP模型通常被分为四个层次&#xff1a;应用层、传输层、网络层和网络接口层。在这个模型中&#xff0c;不同的网络协议负责完成不同的任务&#xff0c;以确保数据可以在网络中高效、可靠地传输。以下是对这张图中每个协议的解释&#xff1a; 应用层…

Tensorflow2.0笔记 - AutoEncoder做FashionMnist数据集训练

本笔记记录自编码器做FashionMnist数据集训练&#xff0c;关于autoencoder的原理&#xff0c;请自行百度。 import os import time import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics, …

Nature子刊 | 首个 双语脑机接口设备能解码西班牙语和英语单词

大脑植入装置首次帮助一名无法用语言表达的双语人士用两种语言进行交流。与大脑植入体相连的AI系统可以实时解码该人试图用西班牙语或英语表达的意思。 这项研究结果发表在5月20日的《Nature Biomedical Engineering》杂志上&#xff0c;让人们深入了解了我们的大脑是如何处理语…

使用printf的两种方法,解决printf不能使用的问题

使用printf的两种方法&#xff0c;解决printf不能使用的问题 一、微库法 我们使用printf前要加上重定向fputc //重定义fputc函数 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)0);//循环发送,直到发送完毕 USART1->DR (uint8_t) ch; return…

Spring MVC+mybatis项目入门:旅游网(四)用户注册——mybatis的配置与使用以及Spring MVC重定向

个人博客&#xff1a;Spring MVCmybatis项目入门:旅游网&#xff08;四&#xff09;用户注册2-持久化 | iwtss blog 先看这个&#xff01; 这是18年的文章&#xff0c;回收站里恢复的&#xff0c;现阶段看基本是没有参考意义的&#xff0c;技术老旧脱离时代&#xff08;2024年…

C#【进阶】特殊语法

特殊语法、值和引用类型 特殊语法 文章目录 特殊语法1、var隐式类型2、设置对象初始值3、设置集合初始值4、匿名类型5、可空类型6、空合并操作符7、内插字符串8、单句逻辑简略写法 值和引用类型1、判断值和引用类型2、语句块3、变量的生命周期4、结构体中的值和引用5、类中的值…

重学java 45.多线程 下 总结 定时器_Timer

人开始反向思考 —— 24.5.26 定时器_Timer 1.概述:定时器 2.构造: Timer() 3.方法: void schedule(TimerTask task, Date firstTime, long period) task:抽象类,是Runnable的实现类 firstTime:从什么时间开始执行 period:每隔多长时间执行一次…

fpga问题整理

1、quartus联合modelsim仿真 无波形 问题&#xff1a; modelsim仿真无波形&#xff0c;打开transcript可以看到警告。 警告&#xff1a; # ** Warning: (vlog-2083) f:/program files/altera/ 13.1/quartus/ eda/sim_lib/ altera_lnsim.sv(22728): Carriage return (0x0D) is…

MySQL之Schema与数据类型优化(五)

Schema与数据类型优化 特殊类型数据 某些类型的数据并不直接与内置类型一致。低于秒级精度的时间戳就是一个例子。另外一个例子是一个IPv4地址。人们经常使用VARCHAR(15)列存储IP地址。然而&#xff0c;它们实际上是32位无符号整数。不是字符串。用小数点将地址分成四段的表示…

用AI比赛助手降维打击数学建模,比赛过程详细介绍,这保研不就稳了吗

数学建模是个小众的赛道&#xff0c;可能很多大学生不知道&#xff0c;简单来说&#xff1a;他能薅学分、保研加分、毕业好找工作(简历上写一辈子)&#xff0c;尤其是基于GPT-4o模型&#xff0c;简直对他们是降维打击。 数学建模每年的比赛非常多&#xff0c;像国赛、美赛、深…

asrpro softspi SD卡读写

采样 50M 1M&#xff1b;采样时间足够长&#xff0c;采样频率1M 避免信息遗漏 引脚 cs pa2 mosi pa3 sck pa5 miso pa6 vcc ->5v gnd ->gnd ARDUINO SD库与移植&#xff08;原本是打算移值tw ch32v103的sd库的&#xff0c;但没有对比&#xff0c;只能选择arduino ; …

Java进阶学习笔记22——泛型方法、通配符和上下限

泛型方法&#xff1a; package cn.ensource.d11_generics_method;public class Test {public static void main(String[] args) {// 泛型方法String res test("Java");System.out.println(res);Dog dog1 test(new Dog());System.out.println(dog1);}// 泛型方法pub…

手机上制作证件照

最近由于需要给老姐弄一组证件照&#xff0c;找了一通手机上的软件&#xff0c;找到一款性价比较高的&#xff0c;详细流程记录下来。vx小程序上搜索"泰世茂证件照"&#xff0c;打开首页如下图所示∶ 单击"开始制作" &#xff0c;选择一个证件照类别&#…

Paddle 傅里叶变换基础及领域应用

Paddle 傅里叶变换基础及领域应用 1. 傅里叶变换基础 1.1 傅里叶变换简介 傅里叶变换是一种重要的信号处理技术&#xff0c;它可以将一个信号从时域转换到频域。在频域中&#xff0c;信号的频率特性更加明显&#xff0c;有利于分析和处理。傅里叶变换的基本思想是将一个信号…