关于 TCP 并发毗连的几个思考题与试验
副标题#e#
前几天我在新浪微博上出了两道有关 TCP 的思考题,激发了一场接头 http://weibo.com/1701018393/eCuxDrta0Nn 。
第一道低级题目是:
有一台呆板,它有 一个 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()/connect() 或 accept() 来建设 TCP 毗连,那么每个毗连至少要占用一个文件描写符(file descriptor)。为什么说“至少”?因为文件描写符可以复制,好比 dup();也可以被担任,好比 fork();这样大概呈现系统里边同一个 TCP 毗连有多个文件描写符与之对应。据此,许多人给出的第 一题谜底是:并发毗连数受限于系统能同时打开的文件数目标最大值。这个谜底在实践中是正确的,却 不切合原题意。
假如抛开操纵系统层面,只思量 TCP/IP 层面,成立一个 TCP 毗连有哪些开销 ?理论上最小的开销是几多?思量两个场景:
1. 假设有一个 TCP 处事措施,向这个措施乐成 提倡毗连需要做哪些工作?换句话说,如何才气让这个 TCP 处事措施认为有客户毗连到了它(让它的 accept() 挪用正常返回)?
2. 假设有一个 TCP 客户端措施,让这个措施乐成成立随处事器的 毗连需要做哪些工作?换句话说,如何才气让这个 TCP 客户端措施认为它本身已经毗连随处事器了( 让它的 connect() 挪用正常返回)?
以上这两个问题问的不是如何编程,如何挪用 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 的 TCP segment
1c. 向对方发送一个包括 ACK 的 segment
在 做完这三件工作之后,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
在做完这两件事 情(收一个 SYN、发一个 SYN+ACK)之后,TCP 客户端措施会认为毗连已成立。而做这三件工作并不占 用 faketcp 处事端的资源(?)换句话说,faketcp 处事端可以一直反复做这两件事件,接管不计其 数的 TCP 毗连,而 faketcp 处事端本身毫发无损。很快我们将看到如何用措施来实现这一点。
基于对以上两个问题的阐明,说明单独谈论“TCP 并发毗连数”是没有意义的,因为毗连数基 本上是要几多有几多。更有意义的机能指标或者是:“每秒钟收发几多条动静”、“每秒钟收发几多字 节的数据”、“支持几多个勾当的并发客户”等等。
#p#副标题#e#
faketcp 的措施实现
代码见: https://github.com/chenshuo/recipes/tree/master/faketcp 可以直接用 make 编译
为了验 证我上面的说法,我写了几个小措施来实现 faketcp,这几个措施可以提倡或接管不行胜数的 TCP 并 发毗连,而且不用耗操纵系统资源,连动态内存分派都不会用到。
我家里有一台运行 Ubuntu Linux 10.04 的 PC 机,hostname 是 atom,所有的试验都在这上面举办。
家里试验情况的网 络设置是:
#p#分页标题#e#
陈硕在《谈一谈网络编 程进修履历》中曾提到“可以用 TUN/TAP 设备在用户态实现一个能与本机点对点通信的 TCP/IP 协议 栈”,这次的试验正好可以用上这个步伐。
试验的网络设置是:
详细做法 是:在 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 了。
代码见 https://github.com/chenshuo/recipes/blob/master/faketcp/icmpecho.cc
个中响应 icmp echo request 的函数在 https://github.com/chenshuo/recipes/blob/master/faketcp/faketcp.cc#L57 这个函数在后头的程 序中也会用到。
运行要领,打开 3 个呼吁行窗口:
1. 在第 1 个窗口运行 sudo ./icmpecho ,措施显示
allocted tunnel interface tun0
2. 在第 2 个窗口运行
$ sudo ifconfig tun0 192.168.0.1/24
$ sudo tcpdump -i tun0
3. 在第 3 个 窗口运行
$ ping 192.168.0.2
$ ping 192.168.0.3
$ ping 192.168.0.234
发明每个 192.168.0.X 的 IP 都能 ping 通。
第二步:实现拒绝 TCP 毗连的成果,即在收到 SYN TCP segment 的时候发送 RST segment。
代码见 https://github.com/chenshuo/recipes/blob/master/faketcp/rejectall.cc
运行要领,打开 3 个呼吁行窗口,头两个窗口的操纵与前面沟通,运行的 faketcp 措施是 ./rejectall
3. 在 第 3 个窗口运行
$ nc 192.168.0.2 2000
$ nc 192.168.0.2 3333
$ nc 192.168.0.7 5555
发明向个中任意一个 IP 提倡的 TCP 毗连都被拒接了。
第三步:实 现接管 TCP 毗连的成果,即在收到SYN TCP segment 的时候发回 SYN+ACK。这个措施同时处理惩罚了毗连 断开的环境,即在收到 FIN segment 的时候发回 FIN+ACK。
代码见 https://github.com/chenshuo/recipes/blob/master/faketcp/acceptall.cc
运行要领,打开 3 个呼吁行窗口,步调与前面沟通,运行的 faketcp 措施是 ./acceptall。这次会发明 nc 能和 192.168.0.X 中的每一个 IP 每一个 PORT 都能连通。还可以在第 4 个窗口中运行 netstat –tpn , 以确认毗连确实成立起来了。假如在 nc 中输入数据,数据会会萃在操纵系统中,表示为 netstat 显 示的发送行列(Send-Q)的长度增加。
第四步:在第三步接管 TCP 毗连的基本上,实现吸收数 据,即在收到包括 payload 数据 的 TCP segment 时发回 ACK。
代码见 https://github.com/chenshuo/recipes/blob/master/faketcp/discardall.cc
运行要领,打开 3 个呼吁行窗口,步调与前面沟通,运行的 faketcp 措施是 ./acceptall。这次会发明 nc 能和 192.168.0.X 中的每一个 IP 每一个 PORT 都能连通,数据也能发出去。还可以在第 4 个窗口中运行 netstat –tpn ,以确认毗连确实成立起来了,而且发送行列的长度为 0。
这一步已包办理了 前面的问题 2,饰演任意 TCP 处事端。
第五步:办理前面的问题 1,饰演客户端向 atom 提倡 任意多的毗连。
代码见 https://github.com/chenshuo/recipes/blob/master/faketcp/connectmany.cc
这一步的运行 要领与前面差异,打开 4 个呼吁行窗口。
1. 在第 1 个窗口运行 sudo ./connectmany 192.168.0.1 2007 1000 ,暗示将向 192.168.0.1:2007 提倡 1000 个并发毗连。
措施显示
allocted tunnel interface tun0
press enter key to start connecting 192.168.0.1:2007
查察本栏目
2. 在第 2 个窗口运行
$ sudo ifconfig tun0 192.168.0.1/24
$ sudo tcpdump -i tun0
3. 在第 3 个窗口运行一个能吸收并发 TCP 毗连的处事措施,可以是 httpd,也可以是 muduo 的 echo 或 discard 示例,措施应 listen 2007 端口。
4. 回到第 1 个窗口中敲回车,然后在第 4 个窗口顶用 netstat -tpn 来调查并发毗连 。
有乐趣的话,还可以继承扩展,做更多的有关 TCP 的试验,以进一步加深领略,验证操纵系 统 TCP/IP 协议栈面临差异输入的行为。甚至可以按我在《谈一谈网络编程进修履历》中提议的那样, 实现完整的 TCP 状态机,做出一个简朴的 mini tcp stack。
第一道题的谜底:
#p#分页标题#e#
在只考 虑 IPv4 的环境下,并发数的理论上限是 2**48。思量某些 IP 段被保存了,这个上界可适当缩小,但 数量级稳定。实际的限制是操纵系统全局文件描写符的数量,以及内存巨细。
一个 TCP 毗连有 两个 end points,每个 end point 是 {ip, port},题目说个中一个 end point 已经牢靠,那么留下 一个 end point 的自由度,即 2 ** 48。客户端 IP 的上限是 2**32 个,每个客户端IP提倡毗连的上 限是 2**16,乘到一起得理论上限。
即便客户端利用 NAT,也不影响这个理论上限。(为什么 ?)
在真实的 Linux 系统中,可以通过调解内核参数来支持上百万并发毗连,详细做法见:
http://urbanairship.com/blog/2010/09/29/linux-kernel-tuning-for- c500k/
http://www.metabrew.com/article/a-million-user-comet-application-with- mochiweb-part-3
(.完.)