不是。65535 只是端口号的取值上限(16 位字段),而 TCP 连接靠四元组区分,不是靠本地端口单独标识。服务端的连接数上限取决于机器资源,理论上远超 65535;真正容易撞到端口天花板的,是高频短连接的客户端。

问题的本质:四元组而非端口

一条 TCP 连接由四元组唯一标识:

源 IP、源端口、目的 IP、目的端口

假设服务器监听 192.168.1.100:8080,以下三条连接服务端 IP 和端口完全相同,但仍是三条独立连接:

(10.0.0.1, 50000, 192.168.1.100, 8080)
(10.0.0.1, 50001, 192.168.1.100, 8080)
(10.0.0.2, 50000, 192.168.1.100, 8080)

服务端只有一个监听 socket,但每次 accept() 内核都生成一个新的已连接 socket,按四元组区分,多个连接共享同一端口毫无冲突。纯从组合空间算:固定服务端 IP 与端口后,客户端 IP 有约 2^32 种、客户端端口有 2^16 种变化,理论上限是天文数字。

实际瓶颈在哪里

理论归理论,机器资源先顶不住:

  • 文件描述符(FD):Linux 中 socket 也是文件,每条已建立连接占一个 FD。进程上限不够时报 Too many open files,需调 ulimit -n 与 fs.file-max。
  • 内存:每条连接内核要维护 socket 结构、TCP 控制块(TCB)、收发缓冲区。空闲连接占用有限,一旦活跃收发,缓冲区与应用层对象的内存开销会显著上升。
  • 内核队列:net.core.somaxconn 与 listen(fd, backlog) 共同决定全连接队列上限(取两者较小值),tcp_max_syn_backlog 影响半连接队列。它们限制的是建连阶段的排队能力,不是 ESTABLISHED 总数。
  • CPU 与网卡:活跃连接带来系统调用、协议解析、加解密、中断处理的压力。

客户端是另一本账。固定源 IP 反复连接同一个目标时,四元组里只剩源端口可变。Linux 默认临时端口范围约 2.8 万个:

sysctl net.ipv4.ip_local_port_range   # 常见默认 32768 60999

端口耗尽时报 Cannot assign requested address。更糟的是主动关闭方会进入 TIME_WAIT,连接在一段时间内不可复用,高频短连接场景下临时端口被大量 TIME_WAIT 占着,可用空间进一步压缩。若流量经过 NAT 网关,共享公网 IP 的源端口池和连接跟踪表也可能先成为瓶颈。

从 C10k 到 C10M 的演进思路

C10k 问题的核心是”一万个并发连接怎么处理”,早期一台机器一个连接一个线程的模型根本扛不住。演进大致分几步:

  1. I/O 多路复用:select/poll 到 epoll/kqueue,一个线程管理海量连接,只处理有事件的就绪 socket。这是 Nginx、Netty 的基础。
  2. 减少每连接开销:连接池与 HTTP Keep-Alive 复用连接、降低建连频率;零拷贝(sendfile)减少数据在内核与用户态之间的搬运。
  3. 内核参数与硬件配套:调大 FD 上限、扩临时端口范围、调 backlog,配合多队列网卡、RSS 把中断分摊到多核。
  4. C10M 的方向:千万级连接下内核协议栈本身成为瓶颈,思路转向用户态协议栈(DPDK、XDP、eBPF)、内核旁路,把包处理从内核搬到用户态或网卡。

对绝大多数业务,务实的优先级是:先开连接复用和连接池,再调 FD 与端口范围,最后才考虑改内核参数。看到 TIME_WAIT 堆积,第一反应应是”是不是在频繁建短连接”,而不是去动 tcp_tw_reuse。

小结

  • 65535 是单端口字段的上限;连接由四元组区分,服务端单机承载量理论远超此数。
  • 服务端瓶颈在 FD、内存、CPU 与内核队列;客户端连同一目标的瓶颈在临时端口范围与 TIME_WAIT;中间过 NAT 还要看网关的端口池和连接跟踪表。
  • C10k→C10M 的主线:I/O 多路复用 → 连接复用与零拷贝 → 内核调参与多核网卡 → 用户态协议栈/内核旁路。