TCP 连接极限在哪里

2024-08-26 pv

今早,同事遇到一个关于网络连接数的问题,咨询我。

我看他在看 dperf🔗,一个高性能的网络压测工具。

他指着其中一个数字问:几十亿的 HTTP 并发连接数?一台电脑一共才几个端口?

他这个问题把我问住了。虽然我也学过计算机网络,知道 TCP 是一种基于连接的传输层协议,但似乎没有深入思考过,一条 TCP 连接究竟意味着什么?

今天这篇文章,就是想通过寻找单台机器 TCP 连接数的极限,顺便讲清楚 TCP 连接这个东西。

1. TCP 概述

根据 维基百科🔗 维基百科定义,TCP (传输控制协议, Transmission Control Protocol) 是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP 是传输层协议,在五层网络划分中属于第四层。下层的网络层是 IP 协议,上层的应用层,使用最广泛的,是 HTTP,不过最新的 HTTP/3 已经开始使用基于 UDP 的 QUIC 实现。

得益于互联网的快速发展,TCP 被广泛使用。在面试中,TCP 经常是重点考察的部分,这也侧面说明了它的重要性。

我们都知道,网络两端的发送者和接收者,可以在这个星球的任意角落。双向的通信,要做到低延迟和高可靠,几乎是不可能完成的任务。为了实现这些功能,TCP 采取了一系列复杂而精妙的设计,结果就是概念众多,实现复杂。

其中连接,就是 TCP 最主要的一个特征。不过与直觉不同,这里的连接,是逻辑层面上的,并非真实的物理连接。这是可以理解的,网络中设备众多,不可能任意两台设备间都直接相连,否则可扩展性就太差了。

那么 TCP 中的连接,究竟意味着什么?

2. TCP 连接含义

TCP 协议承载的功能是可靠的信息传递,可靠意味着,数据不会丢失,不会乱序,不会存在不准确的问题。

TCP 连接就是这样一条可靠的信息通道。像是管道(pipe),连接起通信双方,信息像水一样在管道里双向流淌。

这么说可能还是有点抽象。

简单来说,连接,本质上是关于一次通信过程的状态记录和维护

举个简单的例子。

张三和李四需要写信交流。张三和李四各自都要记下对方的地址,同时也要记录上次写信写到哪里,这次从什么地方继续,否则交流就会混乱。如果长时间没有收到对方来信,可能中间邮递员在递送过程中出了差错,还需要重新发一次。

对于 TCP 连接同样如此。

TCP 协议栈通过“三次握手”建立起连接后,操作系统就会维护一系列状态:对端 IP、对端端口、本地 IP、本地端口、序列号、确认号、窗口大小等。伴随双方信息交换,这些状态也会随之变化。因此可以这么说,管理好状态,即控制了连接

而 UDP 协议里就没有连接的概念,每一个数据包发出去就忘记,不需要维护状态,实现起来自然简单,只不过不能保证“可靠”罢了。

既然 TCP 连接需要维护状态,自然会耗费一定的计算和内存资源,因而 TCP 连接的数量不会是无限的

3. TCP 连接极限

回到最初的问题上。

TCP 连接的极限在哪里?

通常来说,这取决于多个因素,包括操作系统限制、可用资源、网络条件和程序设计等。

3.1 端口数量

TCP 中,每个连接由一个四元组构成,即(客户端 IP、客户端端口、服务端 IP、服务端端口)。

客户端可以使用的端口通常在 1024-65535 之间。因此理论上,一个客户端可以同时建立 6 万多条连接。

服务端通常监听一个特定端口,但可以处理多个来自客户端的连接,理论上等同于(客户端 IP 数 * 客户端端口数),大概是 2^48。

3.2 文件描述符

在 Linux 系统中,每个 TCP 连接都需要一个文件描述符(File Descriptor),而最大描述符数量是有限制的。Linux 通常默认为 1024,这对服务器来说远远不够,好在这个数字可以调整,如 ulimit -n 命令。

全局的文件描述符限制同样影响单机能建立的最大连接数,这个可以借助 /proc/sys/fs/file-max 查看。

3.3 内存和资源限制

上文提到,连接就是一系列状态记录。状态维护就需要内存和计算资源,因此最大连接数同样受到内存和 CPU 性能的约束。

3.4 网络带宽

网络带宽决定的是数据吞吐量,连接数过大,会影响数据传递的效率。因此带宽小的机器,能支撑的最大连接数也较小。

3.5 操作系统 TPC/IP 栈

Linux 的网络栈对于总的 TCP 连接数也有限制,但可以通过内核参数调整。例如 /proc/sys/net/ipv4/ip_local_port_range 可以配置本地端口范围,/proc/sys/net/core/somaxconn 可以配置服务端最大连接数。

这就涉及比较深层次的网络栈优化了。

3.6 应用程序设计

程序设计本身要支持高并发,否则 TCP 连接很容易遇到瓶颈。

现在主流的架构是事件驱动(event-driven),Linux 下通常采用 epoll。

3.7 小结

上述分析,都是基于 Linux 系统自带的 TCP/IP 协议栈

那么,dperf 如何实现单机几十亿的并发连接?

答案在 DPDK (Data Plane Development Kit) 上

DPDK 绕过了操作系统内核的 TCP/IP 协议栈,直接在用户态处理网卡驱动接收到的数据包。也就是说,DPDK 是用户自己维护连接的状态,而不是委托系统内核,因此技术难度和想象空间都更大。

于是传统的经验失效了。

4. 总结

简单总结下,一条 TCP 连接,就是一组状态,连接的极限,取决于系统究竟能同时处理好多少个这样的状态集合。

起初我也觉得,单机几十亿的并发连接是不可能的。如果基于传统的 TCP/IP 协议栈的确如此,因为系统内核难以维护如此多的状态。

但所有的经验和结论都是有条件的,极限就是用来被打破的。

理论和工程的边界究竟在哪里?

答案也许是,没有边界。

(完)

参考

  1. baidu/dperf: dperf is a 100Gbps network load tester.🔗
  2. 传输控制协议 - 维基百科,自由的百科全书🔗
在 GitHub 上编辑本页面

最后更新于: 2024-11-20T09:44:17+08:00