【技术实操】银河高级服务器操作系统实例分享,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…

力扣题解记录

三元组队列、取出元组中的元素&#xff1a;腐烂的橘子

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…

axios的配置项及含义

axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于浏览器和 Node.js。它有许多配置项可以自定义 HTTP 请求。以下列举 axios 中的五个常用配置项及其含义&#xff0c;并提供示例代码&#xff1a; 1. method 含义&#xff1a;指定请求方法&#xff08;例如 GET, POST,…

基于 Windows 的记事本简单功能开发及部署发布--迭代2.1

基于上篇文章基于 Windows 的记事本简单功能开发及部署发布--迭代2.0-CSDN博客 更新完成以下功能点&#xff1a; 1.更新字体到json文件中&#xff0c;下次打开应用能够继续生效。

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…

结构型设计模式之装饰模式

文章目录 概述装饰模式原理代码案例小结 概述 装饰模式(decorator pattern) 的原始定义是&#xff1a;动态的给一个对象添加一些额外的职责. 就扩展功能而言,装饰器模式提供了一种比使用子类更加灵活的替代方案。 装饰模式原理 装饰模式中的角色&#xff1a; 抽象构件角色 …

高精度定位平板主要应用在哪些领域

高精度定位平板是一种集成了高精度定位技术和强大计算能力的设备&#xff0c;能够提供亚米级甚至厘米级的定位精度。其应用领域广泛&#xff0c;涵盖测绘、精准农业、工程建设、地理信息系统&#xff08;GIS&#xff09;、公共安全等多个方面。这种设备凭借其高精度和耐用性&am…

设置MySQL like查询时不区分大小写

背景介绍 MySQL like查看结果出现了区分大小写的情况&#xff0c;实际需求是查询的时候不去区分大小写&#xff01; 原因 MySQL的LIKE查询默认情况下是不区分大小写的。如果LIKE查询是区分大小写的&#xff0c;是因为列使用了区分大小写的字符集或校对规则。 解决办法 在l…

2024年上半年系统架构设计师真题(部分考场回忆版)

一、综合知识 1.常见的ADL语言&#xff0c;四个中选三个 2.EAI的四个层次从低到高依次是 3.反规范化设计属于数据设计的那个阶段 A.需求分析 B.概念设计 C.逻辑设计 D.物理设计 4.嵌入式往往具有实时特征&#xff0c;其典型架构 5.交换机位于OSI七层模型的那一层 6.常见…

肮脏至极的处事技巧,看懂少走十年弯路

处世技巧&#xff0c;也是肮脏至极的处事技巧&#xff0c;看懂少走十年弯路。有用的&#xff01;&#xff01;&#xff01; 1、手机响5秒再接。 2、永远提前15分钟。 3、坐领导的车&#xff0c;不要玩手机&#xff0c;只坐在副驾驶&#xff0c;不坐后排。 4、朋友给你介绍了…

深入探讨 Java 8 集合操作:全面解析 Stream API 的强大功能

深入探讨 Java 8 集合操作&#xff1a;全面解析 Stream API 的强大功能 Java 8 引入的 Stream API 使得集合操作更加简洁和高效。本文通过详细示例&#xff0c;展示如何利用 Stream API 进行各种集合操作&#xff0c;包括遍历、转换、过滤、排序、分组、去重等。 1. 遍历集合…

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

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