c++ - TCP 服务器不接受客户端发出的正确数量的连接,侦听积压较少
问题描述
我编写了一个简单的客户端程序,在非常短的时间内使用 100 个线程发出总共 10000 个连接。还有一个简单的服务器程序,listen backlog
设置为 20,使用 epoll 来接受任何新连接并记录总连接数。我曾经ulimit -n
确保它大于 20000,所以应该有足够的 fd 资源。
但是在服务器程序accept
像 8800、9400(不固定)连接之后,它只是停止接受任何新连接。客户端使用阻塞connect
建立连接,这10000次调用全部connect
返回成功。然后一切都冻结了,没有更多的数据包(没有重新传输),没有更多的接受连接。
但是一旦我关闭了客户端程序,服务器程序在关闭一些连接后开始接受剩余的连接(并最终接受了所有 10000 个连接并关闭了所有这些连接)。
当我更改backlog
为 100 或更大时,所有 10000 个连接都被接受,没有任何问题。(所以不是fd资源问题)
我知道当接受队列已满时,linux 可能会忽略传入ACK
的 3-Way 握手,让客户端的连接建立但服务器的连接仍未建立,然后让重传机制工作。最终服务器重新传输SYN/ACK
pakcet,ACK
如果服务器的接受队列可用,则客户端响应以重新建立此连接。如果队列中没有可用空间,服务器将ACK
再次忽略。
但是当我使用wireshark监控这些重传时,我发现只是发生了少量的SYN/ACK
重传(比如100~200,远少于丢失的连接数,在500~1500之间),并且只重传了一次或两次, 都小于中指定的值/proc/sys/net/ipv4/tcp_synack_retries
。我检查了其中一些重传的SYN/ACK
数据包,所有这些都是ACK
从客户端接收的。但是SYN
来自客户端的重传数据包数量很大。
那么底层细节是什么?
解决方案
当 backlog 已满并启用 SYN cookie 时,内核将激活临时 SYN Flood 模式。此图显示了 cookie 的工作原理(来源):
当 SYN flood 被激活时,所有从客户端发送的 ACK 都将被丢弃。服务器只向客户端发送 SYN/ACK 和 cookie,cookie 缓存表将比套接字表小得多,以保持活动连接。在这种情况下,在客户端,套接字被认为已建立,但在服务器端并没有真正打开任何东西(半开放连接)。
当客户端应用关闭socket时,会向服务器发送一个设置了ACK标志的FIN包,里面有一个cookie,服务器看到连接有ACK,此时如果backlog没有满,服务器会尝试从 cookie 重建连接。如果 cookie 有效(在有效的往返行程内),则套接字将被添加到积压队列中,并且可以作为普通套接字处理。
这意味着,当 SYN cookie 模式被激活并且 backlog 队列未满时,如果客户端通过半开套接字向服务器发送一些数据,accept() 函数将返回新的传入连接。
推荐阅读
- tensorflow - 如何编写自定义 keras 层来重新采样 3D 体积?
- node.js - 如何修复应该显示数据的授权的 [object Object] 响应?
- python - xarry 绘图 - 具有最大值的像素不会被设置的级别淹没
- python - re.match() 在python中具有固定模式
- google-maps - 为什么谷歌地图在加载时会切断这个网址的最后两个地址?
- apache-nifi - 在分布式 nifi 集群和电子邮件通知上合并流文件的问题
- laravel - 合并 3 个集合以每行返回一个对象
- google-signin - 用于 Google 登录的 SRI
- kubernetes - Grafana helm 通知配置
- hp-uft - 使用拖放方法