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

news2024/12/25 9:18:48

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.coloradmin.cn/o/1697232.html

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

相关文章

Yolov8训练自己的数据集(脱离ultralytics库)

最近在整理关于yolov8的相关内容&#xff0c;有个很大的问题&#xff0c;抛开yolov8性能不谈&#xff0c;yolov8代码的使用灵活性不如yolov5&#xff0c;尤其是对于一些新手或者对yolo框架不是很熟悉的人(这也是因人而异&#xff0c;有些人可能会喜欢v8代码的使用方式)。比如在…

牛客网刷题 | BC94 反向输出一个四位数

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 将一个四位数&…

1.int 与 Integer 的简单区别

蓝桥杯刷题从此开始&#xff1a; 第一题就是两个数的和&#xff0c;个人看来主要考察 int与integer 的区别&#xff1b; 这是我提交的答案&#xff0c;竟然会报错&#xff1a; import java.util.*; //输入A、B&#xff0c;输出AB。 class add {public static void main(String …

MT7628原厂Uboot修改交互串口

工作中&#xff0c;遇到用户用Skylab的SKW92A模组&#xff0c;在参考设计时&#xff0c;将UART接口预留错的情况&#xff0c;对于这种情况&#xff0c;需要将原厂SDK默认的交互串口UART0&#xff0c;改为UART1。在开发过程中&#xff0c;经常需要在Uboot阶段升级固件&#xff0…

5.25机器人基础-空间描述和变换1

参考资料&#xff1a;《机器人学导论》John.J.Craig 彻底搞懂“旋转矩阵/欧拉角/四元数”&#xff0c;让你体会三维旋转之美_欧拉角判断动作-CSDN博客 机器人操作的定义是指通过某种机构使零件和工具在空间运动。因此&#xff0c;对于坐标系的定义显得尤为重要&#xff0c;相…

【docker】Docker的基本指令和HTML/PYTHON/C++的简单创建示例

目录 &#x1f30a;1. 什么是 Docker&#xff1f; &#x1f30a;2. Docker 安装 &#x1f30a;3. Docker基本指令 &#x1f30a;4. Docker 创建示例【联网情况】 &#x1f30d;4.1 示例&#xff1a;HTML &#x1f30d;4.2 示例&#xff1a;Python 脚本 &#x1f30d;4.3…

log4j2远程代码执行

漏洞复现 漏洞复现2 这个框架不是web框架了&#xff0c;不是服务器web网站框架了&#xff0c;是java日志框架&#xff0c;就是记录日志信息&#xff0c;每一个程序都有一个日志文件&#xff0c;这个就是java里面记录日志的一个框架&#xff0c;它存在的点也是日志框架那几个代…

Python语法(全)

前言&#xff1a; 下面是Python基本的语法&#xff0c;大家耐心观看&#xff01; 1.基础语法 1.1字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面 1.2字符串 字符串&#xff08;string&#xff09;&#xff0c;又称文本&#xff…

防火墙——域网络、专用网络、公用网络

在防火墙设置中&#xff0c;域网络、专用网络和公用网络是指计算机连接到网络时所处的不同环境。每种环境都有不同的安全级别和配置。 1、域网络&#xff08;宽松&#xff09; 域网络是指计算机加入了一个Windows域&#xff08;Domain&#xff09;环境&#xff0c;这通常在企业…

v-rep---script-function

作用&#xff0c;实现&#xff0c;参数讲解。 script-function标签 作用 问题&#xff1a;如何在插件的接口中调用lua脚本中定义的函数&#xff1f; 用于声明一个函数&#xff0c;这个函数的作用是通过v-rep提供的接口sim::callScriptFunctionEx()调用脚本的函数&#xff0…

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

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

直流电机的基本原理与结构

一些基本定理 磁感应强度&#xff08;又称磁通密度&#xff09;B —— 表征磁场强弱及方向的物理量。单位&#xff1a;T 右手定则&#xff1a;伸开右手&#xff0c;使拇指与其余四个手指垂直&#xff0c;并且都与手掌在同一平面内&#xff1b;让磁感线从手心进入&#xff0c;…

软件设计师基础知识难点总结

软件设计师基础知识难点 I/O设备管理软件一般分为4个层次&#xff0c;如下图所示。 用户进程与设备无关的系统软件设备驱动程序中断处理程序硬件 直接查询控制 分为有无条件传送和程序查询方式&#xff0c;都需要通过CPU执行程序来查询外设的状态&#xff0c;判断外设是否准备好…

牛客热题:有效括号

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;有效括号题目链接方法一&#x…

数据结构--二叉搜索树

目录 二叉搜索树的概念 二叉树的实现 结点类 函数接口总览 实现二叉树 二叉搜索树的应用 K模型 KV模型 二叉搜索树的性能分析 二叉搜索树的概念 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种特殊的二叉树&#xff0c;其具有以下几…

CSP俄罗斯方块(简单易懂)

开始将题目理解成了&#xff0c;开始的列应该是从输入图案的最左端开始计算&#xff0c;将前面所有的空列都删掉&#xff0c;代码如下&#xff1a; #include<bits/stdc.h> using namespace std; const int N 1e410; const int M 1e510; int a[20][20]; int b[5][5];int…

【yolov10】使用自己的数据集训练目标检测模型

【yolov10】使用自己的数据集训练目标检测模型 一、anaconda安装二、环境配置三、数据集制作1、labelimg的安装2、使用labelimg 四、正片1、下载yolov10源码2、数据集目录3、训练4、推理 一、anaconda安装 直接参考前一篇博客&#xff1a; https://blog.csdn.net/m0_71523511/…

八国多语言微盘微交易所系统源码 单控点控 K线完好

安装环境linux NGMySQL5.6PHP7.2&#xff08;函数全删&#xff09;pm2管理器&#xff08;node版本选择v12.20.0&#xff09; config/ database.php 修改数据库链接 设置运行目录 public 伪静态thinkphp

【图解IO与Netty系列】IO的同步与异步、阻塞与非阻塞,Linux五种IO模型

IO的同步与异步、阻塞与非阻塞&#xff0c;Linux五种IO模型 IO的同步与异步&#xff0c;阻塞与非阻塞阻塞IO与非阻塞IO同步IO与异步IO Linux五种IO模型BIONIOIO多路复用信号驱动IOAIO IO的同步与异步&#xff0c;阻塞与非阻塞 我们有时会看到类似于同步阻塞式IO、同步非阻塞式…

【python】python 全国5A级景区数据采集与pyecharts可视化(源码+数据+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…