Linux多线程服务端编程:使用muduo C++网络库 学习笔记 附录D 关于TCP并发连接的几个思考题与试验

前几天作者在新浪微博上出了两道有关TCP的思考题,引发了一场讨论(http://weibo.com/1701018393/eCuxDrtaONn)。

第一道初级题目是:有一台机器,它有一个IP,上面运行了一个TCP服务程序,程序只侦听一个端口,问:从理论上讲(只考虑TCP/IP这一层面,不考虑IPv6)这个服务程序可以支持多少并发TCP连接?(答65536上下的直接出局)

具体来说,这个问题等价于:有一个TCP服务程序的地址是1.2.3.4:8765,问它从理论上能接受多少个并发连接?

第二道进阶题目是:一台被测机器A,功能同上,同一交换机上还接有一台机器B,如果允许B的程序直接收发以太网frame,问:让A承担10万个并发TCP连接需要用多少B的资源?100万个呢?

从讨论的结果看,很多人做出了第一道题,而第二道题则几乎无人问津。这里先不公布答案(第一题答案见文末),让我们继续思考一个本质的问题:一个TCP连接要占用多少系统资源?

在现在的Linux操作系统上,如果用socket(2)或accept(2)来创建TCP链接,那么每个连接至少要占用一个文件描述符(file descriptor)。为什么说“至少”?因为文件描述符可以复制,比如dup();也可以被继承,比如fork();这样可能出现系统中同一个TCP连接有多个文件描述符与之对应。据此,很多人给出的第一题答案是:并发连接数受限于系统能同时打开的文件数目的最大值。这个答案在实践中是正确的,却不符合原题意。

如果抛开操作系统层面,只考虑TCP/IP层面,建立一个TCP连接有哪些开销?理论上最小的开销是多少?考虑两个场景:
1.假设有一个TCP服务程序,向这个程序成功发起连接需要做哪些事情?换句话说,如何才能让这个TCP服务程序认为有客户连接到了它(让它的accept(2)调用正常返回)?

2.假设有一个TCP客户端程序,让这个程序成功建立到服务器的连接需要做哪些事情?换句话说,如何才能让这个TCP客户端程序认为它自己已经连接到服务器了(让它的connect(2)调用正常返回)?

以上这两个问题问的不是如何编程,如何调用Sockets API,而是问如何让操作系统的TCP/IP协议栈认为任务已经成功完成,连接已经成功建立。

学过TCP/IP协议,理解三路握手的读者想必明白,TCP连接是虚拟的连接,不是电路连接。维持TCP连接理论上不占用网络资源(会占用两头程序的系统资源)。只要连接的双方认为TCP连接存在,并且可以互相发送IP packet,那么TCP连接就一直存在。

对于问题1,向一个TCP服务程序发起一个连接,客户端(为明白起见,以下称为faketcp客户端)只需要做三件事情(三路握手):
1a.向TCP服务程序发一个IP packet,包含SYN的TCP segment;

1b.等待对方返回一个包含SYN和ACK的TCPsegment;

1c.向对方发送一个包含ACK的segment。

faketcp客户端在做完这三件事情之后,TCP服务器程序会认为连接已建立。而做这三件事情并不占用客户端的资源,如果faketcp客户端程序可以绕开操作系统的TCP/IP协议栈,自己直接发送并接收IP packet或Ethernet frame的话。换句话说,faketcp客户端可以一直重复做这三件事情,每次用一个不同的IP:PORT,在服务端创建不计其数的TCP连接,而faketcp客户端自己毫发无损。我们很快将看到如何用程序来实现这一点。

对于问题2,为了让一个TCP客户端程序认为连接已建立,faketcp服务端也只需要做三件事情:
2a.等待客户端发来的SYN TCP segment;

2b.发送一个包含SYN和ACK的TCP segment;

2c.忽视对方发来的包含ACK的segment。

faketcp服务端在做完头两件事情(收一个SYN、发一个SYN+ACK)之后,TCP客户端程序会认为连接已建立。而做这三件事情并不占用faketcp服务端的资源。换句话说,faketcp服务端可以一直重复做这三件事,接受不计其数的TCP连接,而faketcp服务端自己毫发无损。我们很快将看到如何用程序来实现这一点。

基于对以上两个问题的分析,说明单独谈论“TCP并发连接数”是没有意义的,因为连接数基本上是要多少有多少。更有意义的性能指标或许是:“每秒收发多少条消息”、“每秒收发多少字节的数据”、“支持多少个活动的并发客户”等等。

faketcp的程序实现

为了验证上面的说法,作者写了几个小程序来实现faketcp,这几个程序可以发起或接受不计其数的TCP并发连接,并且不消耗操作系统资源,连动态内存分配都不会用到。代码见recipes/faketcp,可以直接用make编译。

作者家里有一台运行Ubuntu Linux 10.04的PC,hostname是atom,所有的试验都在这上面进行。家里试验环境的网络配置如图D-1所示。
在这里插入图片描述
作者在附录A中曾提到过“可以用TUN/TAP设备在用户态实现一个能与本机点对点通信的TCP/IP协议栈”,这次的试验正好可以用上这个办法。试验的网络配置如图D-2所示。
在这里插入图片描述
具体做法是:在atom上通过打开/dev/net/tun设备来创建一个tun0虚拟网卡,然后把这个网卡的地址设为192.168.0.1/24,这样faketcp程序就扮演了192.168.0.0/24这个网段上的所有机器。atom发给192.168.0.2~192.168.0.254的IP packet都会发给faketcp程序,faketcp程序可以模拟其中任何一个IP给atom发IP packet。

程序分成几步来实现。

第一步:实现ICMP echo协议,这样就能ping通faketcp了。代码见recipes/faketcp/icmpecho.cc。

其中响应ICMP echo request的函数是icmp_input(),位于recipes/faketcp/faketcp.cc。这个函数在后面的程序中也会用到。

运行方法,打开3个命令行窗口:
1.在第1个窗口运行sudo ./icmpecho,程序显示
在这里插入图片描述
2.在第2个窗口运行
在这里插入图片描述
3.在第3个窗口运行:
在这里插入图片描述
注意到每个192.168.0.X的IP都能ping通。

第二步:实现拒绝TCP连接的功能,即在收到SYN TCP segment的时候发送RST segment。代码见recipes/faketcp/rejectall.cc。

运行方法,打开3个命令行窗口,头两个窗口的操作与前面相同,运行的faketcp程序是./rejectall。在第3个窗口运行
在这里插入图片描述
注意到向其中任意一个IP发起的TCP连接都被拒绝了。

第三步:实现接受TCP连接的功能,即在收到SYN TCP segment的时候发回SYN+ACK。这个程序同时处理了连接断开的情况,即在收到FIN segment的时候发回FIN+ACK。代码见recipes/faketcp/acceptall.cc。

运行方法,打开3个命令行窗口,步骤与前面相同,运行的faketcp程序是./acceptall。这次会发现nc能和192.168.0.X中的每一个IP每一个port都能联通。还可以在第4个窗口中运行netstat -tpn,以确认连接确实建立起来了。如果在nc中输入数据,数据会堆积在操作系统中,表现为netstat显示的发送队列(Send-Q)的长度增加。

第四步:在第三步接受TCP连接的基础上,实现接收数据,即在收到包含payload数据的TCP sengment时发回ACK。代码见recipes/faketcp/discardall.cc。

运行方法,打开3个命令行窗口,步骤与前面相同,运行的faketcp程序是discardall。这次会发现nc能和192.168.0.X中的每一个IP每一个port都能连通,数据也能发出去。还可以在第4个窗口中运行netstat -tpn,以确认连接确实建立起来了,并且发送队列的长度为0。

这一步已经解决了前面的问题2,扮演任意TCP服务端。

第五步:解决前面的问题1,扮演客户端向atom发起任意多的连接。代码见recipes/faketcp/connectmany.cc。

这一步的运行方法与前面不同,打开4个命令行窗口:
1.在第1个窗口运行sudo ./connectmany 192.168.0.1 2007 1000,表示将向192.168.0.1:2007发起1000个并发连接。程序显示
在这里插入图片描述
2.在第2个窗口运行
在这里插入图片描述
3.在第3个窗口运行一个能接收并发TCP连接的服务程序,可以是httpd,也可以是muduo的echo或discard示例,程序应listen 2007端口。

4.在第1个窗口中按回车键,再在第4个窗口中用netstat -tpn命令来观察并发连接。

有兴趣的话,还可以继续扩展,做更多的有关TCP的试验,以进一步加深理解,验证操作系统的TCP/IP协议栈面对不同输入的行为。甚至可以按作者在附录A中提议的那样,实现完整的TCP状态机,做出一个简单地mini tcp stack。

第一道题的答案:

在只考虑IPv4的情况下,并发输的理论上限是2 48 ^{48} 48。考虑某些IP段被保留了,这个上界可适当缩小,但数量级不变。实际的限制是操作系统全局文件描述符的数量,以及内存大小。

一个TCP连接有两个end points,每个end point是{ip, port},题目说其中一个end point已经固定,那么留下一个end point的自由度,即2 48 ^{48} 48。客户端IP的上限是2 32 ^{32} 32个,每个客户端IP发起连接的上限是2 16 ^{16} 16,乘到一起得到理论上限。

即便客户端使用NAT,也不影响这个理论上限。

在真实的Linux系统中,可以通过调整内核参数来支持上百万并发连接,具体做法见:
1.http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3。

2.http://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

案例介绍:汽车售后服务网络构建与信息抽取技术应用(开源)

一、引言 在当今竞争激烈的汽车行业中,售后服务的质量已成为品牌成功的关键因素之一。作为一位经验丰富的项目经理,我曾参与构建一个全面的汽车售后服务网络,旨在为客户提供无缝的维修、保养和配件更换服务。这个项目的核心目标是通过高效的…

使用ES检索PDF或Word等格式文件方案

#大数据/ES #经验 #方案架构 ES检索PDF/Word等格式文件方案 插件安装 ES有文档预处理插件,但是7.x版本默认发版包不包含这个ingest attachment plugin 。 通过摄取附件插件,Elasticsearch 可以使用 Apache 文本提取库 Tika 提取常见格式的文件附件&a…

C++ //练习 10.2 重做上一题,但读取string序列存入list中。

C Primer(第5版) 练习 10.2 练习 10.2 重做上一题,但读取string序列存入list中。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /******************************************************…

基于Springboot免费搭载轻量级阿里云OSS数据存储库(将本地文本、照片、视频、音频等上传云服务保存)

一、注册阿里云账户 打开https://www.aliyun.com/,申请阿里云账户并完成实名认证(个人)。这种情况就是完成了: 二、开通OSS服务 点击立即开通即可。 三、创建Bucket 申请id和secert: 进去创建一个Accesskey就会出现以…

C++实现简易版http server

mini服务器简介 mini服务器功能 1.实现了GET和POST方法的HTTP request和HTTP respond的构建和发送,使服务器可以完成基本通信功能。 2.使用了线程池技术,使服务器可以一次接收更多的链接和加快了服务器处理数据的速度。 3.实现了简易的CGI&#xff0…

pytorch_神经网络构建6

文章目录 强化学习概念实现qLearning基于这个思路,那么解决这个问题的代码如下 强化学习概念 强化学习有一个非常直观的表现,就是从出发点到目标之间存在着一个连续的状态转换,比如说从状态一到状态456,而每一个状态都有多种的行为&#xff…

外贸网站模板建站

测绘检测wordpress外贸主题 简洁实用的wordpress外贸主题,适合做测绘检测仪器设备的外贸公司使用。 https://www.jianzhanpress.com/?p5337 白马非马衣服WordPress外贸建站模板 白马非马服装行业wordpress外贸建站模板,适用于时间服装企业的官方网站…

新物种、新 CRISPR 系统!Evo 大模型突破全基因组生成,创造生物大模型新标杆...

Evo 由Arc Institute、斯坦福大学和TogetherAI的研究人员开发,是一个长上下文的生物基础模型,能够泛化生物学的基本语言:DNA、RNA 和蛋白质。它能够从单个分子到整个基因组规模(超过650 kb)进行预测任务和多模态长序列…

C语言-柔性数组成员的使用

文章目录 摘要柔性数组成员基本使用细节探究 零长度数组-定长数组-变长数组 摘要 本文先介绍柔性数组成员(flexible array member)的基本使用,然后介绍其内存结构。最后,补充了一些数组相关的其他概念。 柔性数组成员 基本使用 参考: 【C语言内功修炼…

EasyExcel3.1.1版本上传文件忽略列头大小写

1、背景 项目中使用easyExcel3.1.1版本实现上传下载功能,相关数据DTO以 ExcelProperty(value "dealer_gssn_id") 形式规定其每一列的名称,这样的话easyExcel会完全匹配对应的列名,即用户上传文件时,列名写成Dealer_…

LeetCode--42

42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,…

访问修饰符、Object(方法,使用、equals)、查看equals底层、final--学习JavaEE的day15

day15 一、访问修饰符 含义: 修饰类、方法、属性,定义使用的范围 理解:给类、方法、属性定义访问权限的关键字 注意: ​ 1.修饰类只能使用public和默认的访问权限 ​ 2.修饰方法和属性可以使用所有的访问权限 访问修饰符本类本包…

Sqli-labs靶场第16关详解[Sqli-labs-less-16]自动化注入-SQLmap工具注入

Sqli-labs-Less-16 #自动化注入-SQLmap工具注入 SQLmap用户手册:文档介绍 - sqlmap 用户手册 以非交互式模式运行 --batch 当你需要以批处理模式运行 sqlmap,避免任何用户干预 sqlmap 的运行,可以强制使用 --batch 这个开关。这样&#xff0…

UV画贴图时如何去掉一部分

1.纹理先选psd 2. altshift 把要去掉的中选中 选择几何体-隐藏选择

动态规划(算法竞赛、蓝桥杯)--区间DP石子合并与环形石子合并、能量项链

1、B站视频链接&#xff1a;E28【模板】区间DP 石子合并_哔哩哔哩_bilibili 题目链接&#xff1a;石子合并&#xff08;弱化版&#xff09; - 洛谷 #include <bits/stdc.h> using namespace std; const int N310; int n,a[N],s[N]; int f[N][N];//f[i][j]表示从i到j合并…

2.模拟问题——5.星期几与字符串对应

输入输出示例 输入&#xff1a; 9 October 2001 14 October 2001 输出&#xff1a; Tuesday Sunday 【原题链接】 字符串处理 C风格的字符串 字符数组&#xff0c;以’\0‘结尾建议在输入输出语句中使用 C风格的字符串 #include <string> using namespace std;初始化…

智慧公厕:打造智慧城市的环卫明珠

在城市建设中&#xff0c;公共卫生设施的完善和智能化一直是重要环节。而智慧公厕作为智慧城市建设的重要组成部分&#xff0c;发挥着不可替代的作用。本文以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;大量精品案例现场实景实图&#xff0c;解读智慧公厕如何助力打…

Linux:kubernetes(k8s)部署CNI网络插件(4)

在上一章进行了node加入master Linux&#xff1a;kubernetes&#xff08;k8s&#xff09;node节点加入master主节点&#xff08;3&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136420447?spm1001.2014.3001.5501 但是他们显示还是没准备好 看一下…

Android Studio调试功能使用汇总

本文为大家汇总了Android Studio调试功能的使用方法&#xff0c;供大家参考&#xff0c;具体内容如下 1.设置断点 选定要设置断点的代码行&#xff0c;在行号的区域后面单击鼠标左键即可。 2.开启调试会话 点击红色箭头指向的小虫子&#xff0c;开始进入调试。 IDE下方出现De…

你知道什么是 BitMap 吗?

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…