tcp的全连接队列和半连接队列满时,客户端再connect发生的情况

首先简单介绍下tcp的全连接队列(accept queue)和半连接队列(syn queue),
1.客户端发起syn请求时,服务端收到该请求,将其放入到syn queue,然后回复ack+syn给客户端。
2.客户端收到ack+syn,再发送ack给服务端。
3. 服务端从syn queue中查找对应记录,完成连接建立,并且将此记录从半连接队列中删除,然后将建 立的连接塞入到accept queue。

上面就是三次握手建立连接的过程,连接建立后,服务端调用accept从accept queue中取出一条连接。

如果服务端发现accept queue满了后,无法塞入,会出现什么情况呢。
为模拟这种情况,可以先将服务端的accept函数注释掉,这样accept queue中的连接无法被消费,从而很快就满了,而且为了快速达到效果,本人在sysctl.conf中,设置net.core.somaxconn=2,即全连接队列的长度为2.

现在100个客户端程序同时执行,如下所示,可以看到服务端的ESTABLISHED的数量是3,不是2。
在这里插入图片描述
为何ESTABLISHED的数量是3,不是2,即刚好比全连接队列的长度大1,这里本人给出自己的看法,即在最后一个连接(即第3个)建立后,再往accept队列塞的时候,发现队列已经满了,所以就不塞了,但是连接已经建立好了。所以数目是队列的长度+1。

可以看出,全连接队列满了后,会出现SYN_RECV的状态,此状态的出现其实标志着全连接队列已经满了。

再过一段时间,可以发现SYN_RECV的状态消失,如下所示:
在这里插入图片描述
本人的sysctl.conf的内容如下:

net.ipv4.tcp_max_syn_backlog=2
net.ipv4.tcp_abort_on_overflow=0
net.core.somaxconn=2
fs.suid_dumpable=1
vm.swappiness=100
net.ipv4.tcp_synack_retries = 8

其中tcp_max_syn_backlog是半连接队列大小,也是2,但是可以看到上面的SYN_RECV的状态数目不是2,也不是3。这块tcp确实设计的很复杂,硬去理解比较头疼,暂时可以不用去管。

上面SYN_RECV和ESTABLISHED的数量是11,剩余的80多个连接,最终由于connect超时而失败。

下面解释下为何SYN_RECV存在一段时间后,最终都消失了。
首先全连接队列满了后,客户端在发起一次SYN后,客户端将此连接放入syn queue;然后发送ack+syn给客户端,客户端ack给服务端。
此时服务端发现全连接队列已经满了,然后就不再认为客户端的ack有效,其重新发送ack+syn给客户端,并且此次发送的ack+syn的序列号(seq)跟之前一样。
之后客户端收到ack+syn,再发送ack给服务端,如此反复若干次,服务端ack+syn重复的次数跟net.ipv4.tcp_synack_retries有关,该次数默认是5。

服务端这样做的目的在于缓冲,避免一下子上来一堆连接,导致连接队列满,后面再连接直接失败。

在这里插入图片描述
如上图所示,10.0.0.60是客户端,64是服务端,注意Time这一列,看上面的时间,6,7,9,13,22,38,70,134。
可以很快算出间隔为1,2,4,8,16,32,64。
即每次客户端ack后,服务端若判断客户端ack无效,则其再次发送ack+syn的间隔时间要在上次的基础上翻倍。
最后一次,服务端发现全连接队列依然是满的,则认为再给客户端发ack+syn失去了意义,所以将此半连接也删掉,从而看不到SYN_RECV状态了。

下面给出客户端和服务端程序的代码,
服务端的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>#define PORT 8888                                    //侦听端口地址:8888
#define BACKLOG 20                                    //侦听队列长度:2extern void process_conn_server(int s);              //服务器对客户端的处理:读取数据并发送响应字符int main(int argc,char *argv[])
{int ss = 0;                                      //ss = server socket = 服务器socket描述符int cs = 0;                                      //cs = client socket = 客户端socket描述符struct sockaddr_in server_addr;                  //服务器地址结构struct sockaddr_in client_addr;                  //客户端地址结构int ret = 0;                                     //返回值pid_t pid;//进程IDchar buffer[1024];ssize_t size = 0;/***  Step 2 : 建立套接字*/ss = socket(AF_INET,SOCK_STREAM,0);              //创建一个AF_INET族的流类型socketif(ss < 0)                                       //检查是否正常创建socket{perror("socket error\n");exit(EXIT_FAILURE);}/***  Step 3 : 设置服务器地址*  Note:*      htonl():将主机数转换成无符号长整型的网络字节顺序*      htons():将整型变量从主机字节顺序转变成网络字节顺序*/bzero(&server_addr,sizeof(server_addr));          //清零server_addr.sin_family = AF_INET;                 //设置地址族为AF_INETserver_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //本地地址server_addr.sin_port = htons(PORT);               //设置端口号/***  Step 4 : 绑定地址结构到套接字描述符*/ret = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));if (ret < 0)                                      //出错{perror("bind error\n");exit(EXIT_FAILURE);}/***  Step 5 : 设置侦听,侦听队列长度为2,可同时处理两个客户端连接请求*/ret = listen(ss,BACKLOG);if (ret < 0)                                      //出错{perror("bind error\n");exit(EXIT_FAILURE);}/***  Step 6 : 主循环过程*/sleep(6000);for(;;){/* 接收客户端连接 */int addrlen = sizeof(struct sockaddr);cs = accept(ss,(struct sockaddr*)&client_addr,&addrlen);if(cs < 0)                                    //出错{continue;                                 //结束本次循环}sleep(30);/*for(;;){size = read(cs,buffer,1024);                 //从套接字中读取数据放到缓冲区buffer中if(size == 0)                               //没有数据{return;}printf("buffer is %s\n", buffer);}
*/}return 0;
}

客户端的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define PORT 8888                                     //端口地址:8888
#define SERVER_IP "10.0.0.64"extern void process_conn_client(int s);int main(int argc,char *argv[])
{int s = 0;                                        //socket描述符struct sockaddr_in server_addr;                   //服务器地址结构int ret = 0;                                      //返回值char buf[1024] = {0};int i = 0;strcpy(buf, "hello world");/***  Step 2 : 建立套接字*/s = socket(AF_INET,SOCK_STREAM,0);                //创建一个AF_INET族的流类型socketif(s < 0)                                         //检查是否正常创建socket{perror("socket error\n");exit(EXIT_FAILURE);}/***  Step 3 : 设置服务器地址*/memset(&server_addr, 0, sizeof(server_addr));          //清零server_addr.sin_family = AF_INET;                 //设置地址族为AF_INETinet_pton(AF_INET, SERVER_IP, &(server_addr.sin_addr));server_addr.sin_port = htons(PORT);               //设置端口号/***  Step 4 : 将用户输入的字符串类型的IP地址转为整型*///inet_pton(AF_INET,argv[1],&server_addr.sin_addr);/***  Step 5 : 连接服务器*/ret = connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));if(ret < 0){perror("connect error\n");printf("errno is %d\n", errno);exit(EXIT_FAILURE);}printf("connect succeed\n");for(;;){sleep(10000000);sprintf(buf, "hello world, %d", i);i++;write(s,buf,strlen(buf)+1);}close(s);                                         //关闭连接
}

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

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

相关文章

通过adb 命令打印安装在第三方模拟器上的log

1&#xff0c;环境&#xff1a;Windows 11 &#xff0c;第三方模拟器 网易的MuMu 步骤&#xff1a; 1&#xff0c;打开cmd&#xff0c;输入 adb connect 172.0.0.1:7555 2&#xff0c;在cmd&#xff0c;再次输入adb logcat 回车

超详细的社交红人营销玩法来啦!点击查看↓天府锋巢直播产业基地重点服务优势

在刚刚过去的2023年 直播行业听到最多的词“红人营销” 可以算上其中之一 到底什么是红人营销&#xff1f; 红人营销如何做到声量和销量双增长&#xff1f; 企业如何产出 最真实有趣高效的营销内容&#xff1f; 天府锋巢为您解析基地 链主企业「无锋科技」提供的 重点优…

数据结构-上三角矩阵存储方式[0知识掌握]

目标&#xff1a;看完本文章你将会了解上三角矩阵的存储方式以及矩阵中数据的位置索引号如何求 难点&#xff1a;上三角矩阵的公式推导&#xff0c;上三角任意位置对应的存储位置。 一、准备知识 1.求和公式 前n项和&#xff1a;Sn n(a1an)/2 公差&#xff1a;d后项-前项…

winform 入门篇 复合控件

复合控件 自定义控件 除了框架自带的标准控件外&#xff0c;还可以自定义控件 有三种方式: 1 复合控件:将标准控件组合起来 class YourControl : UserControl{} 2 扩展控件:继承于标准控件 class YourControl : Button {} 3 自定义控件:完全地自定义一个控件 class YourCon…

MVVM、MVC、MVP的区别

MVC、MVP 和 MVVM 是三种常见的软件架构设计模式&#xff0c;主要通过分离关注点的方式来组织代码结构&#xff0c;优化开发效率。 在开发单页面应用时&#xff0c;往往一个路由页面对应了一个脚本文件&#xff0c;所有的页面逻辑都在一个脚本文件里。页面的渲染、数据的获取&a…

Linux系统部署可视化数据多维表格APITable并实现无公网IP远程协同办公

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-G5XdKx1vxX0o0PES {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

基于springboot实现人事管理系统项目【项目源码+论文说明】

基于springboot实现人事管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为学校以及一些培训机构&#xff0c;都在用信息化战术来部署线上学习以及线上考试&#xff0c;可以与线下的考试有机的结合在一起&#xff0c;实现基于vue的人事系统在技术…

【详细】OSPF vs RIP

目录 内部网关协议IGP 按照算法原理分 按照适用范围分 两者优缺点比较 RIP协议 RIP的两个版本区别 RIPv2认证功能 RIP协议优缺点 RIP协议路由环路发生原理及处理方式 造成路由环路的原因&#xff1a; 路由环路的危害 路由环路解决方法​编辑 OSPF协议 Router-ID&a…

探索C# 11与.NET 7:入门指南与跨平台开发

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

OpenHarmony开发实例:【新闻客户端】

介绍 本篇Codelab我们将教会大家如何构建一个简易的OpenHarmony新闻客户端&#xff08;JS版本&#xff09;。应用包含两级页面&#xff0c;分别是主页面和详情页面&#xff0c;两个页面都展示了丰富的UI组件&#xff0c;其中详情页的实现逻辑中还展示了如何通过调用相应接口&a…

Day 23 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇

修剪二叉搜索树 给定一个二叉搜索树&#xff0c;同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[L, R]中 (R>L) 。你可能需要改变树的根节点&#xff0c;所以结果应当返回修剪好的二叉搜索树的新的根节点。 ​ 最直接的想法&#xff0…

天工杂志社《天工》杂志社2024年第5期目录

业界翘楚 巍巍者昆仑 煌煌者华夏 乔彦鹏; 6-8 工美史话 日月之光照澈幽冥——墓葬铜镜中柿蒂纹意象辨析 周昕怡;刘春芽; 9-11 西藏传统手工艺技能教育发展历程研究 吕元菊; 12-14 探索研究《天工》投稿&#xff1a;cn7kantougao163.com 传统装饰艺术设计美学的…