首页 > 解决方案 > 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/ACKpakcet,ACK如果服务器的接受队列可用,则客户端响应以重新建立此连接。如果队列中没有可用空间,服务器将ACK再次忽略。

但是当我使用wireshark监控这些重传时,我发现只是发生了少量的SYN/ACK重传(比如100~200,远少于丢失的连接数,在500~1500之间),并且只重传了一次或两次, 都小于中指定的值/proc/sys/net/ipv4/tcp_synack_retries。我检查了其中一些重传的SYN/ACK数据包,所有这些都是ACK从客户端接收的。但是SYN来自客户端的重传数据包数量很大。

那么底层细节是什么?

这是我的代码: 客户端 服务器

标签: c++linuxsocketstcplisten

解决方案


当 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() 函数将返回新的传入连接。


推荐阅读