我编写了一个简单的客户端程序,在非常短的时间内使用100个线程发出了10000个连接。使用epoll将listen backlog
设置为20的简单服务器程序只是为了接受任何新连接并记录总连接数。我使用ulimit -n
来确保它大于20000,因此应该有足够的fd资源。
但是在服务器程序accept
像8800、9400(非固定)连接之后,它只是停止接受任何新连接。客户端使用阻塞connect
建立连接,connect
的所有这10000次调用均返回成功。然后,一切都冻结了,不再有数据包(没有重新传输),也没有接受的连接了。
但是,一旦我关闭了客户端程序,服务器程序在关闭了某些连接后便开始接受剩余的连接(并最终接受了所有10000个连接并关闭了所有这些连接)。
当我将backlog
更改为100或更大时,所有10000个连接都被接受,没有任何问题。 (所以这不是fd资源问题)
我知道,当接受队列已满时,Linux可能会忽略三向握手的传入ACK
,保留客户端的连接已建立,但服务器的连接仍未建立,然后使重传机制起作用。最终,服务器重新传输SYN/ACK
包,如果服务器的接受队列可用,客户端将以ACK
响应以重新建立此连接。如果队列中没有可用空间,服务器将再次忽略ACK
。
但是当我使用Wireshark监视这些重传时,我发现只发生了少量SYN/ACK
重传(例如100〜200,远少于丢失的连接数,它在500〜1500之间),并且仅重传一次或两次,都小于/proc/sys/net/ipv4/tcp_synack_retries
中指定的值。我检查了其中一些重发的SYN/ACK
数据包,所有这些数据包都从客户端收到了ACK
。但是从客户端转发的SYN
数据包数量很大。
那么基本细节是什么?
答案 0 :(得分:1)
当积压已满并且启用了SYN cookie时,内核将激活临时SYN泛洪模式。此图显示了cookie的工作方式(source):
激活SYN Flood后,将从客户端发送的所有ACK丢弃。服务器仅向客户端发送了SYN / ACK和cookie,因此cookie缓存表将比套接字表小得多,以保持活动连接。 在这种情况下,在客户端,套接字被认为是已建立的,但在服务器端并没有真正打开任何东西(半开连接)。
当客户端应用程序关闭套接字时,带有ACK标志设置的FIN数据包将被发送到服务器中,其中包含cookie,服务器会发现该连接具有ACK,此时,如果积压未满,服务器将尝试通过cookie重建连接。如果cookie有效(在有效的往返时间内),则套接字将被添加到积压队列中,并且可以作为普通套接字进行处理。
这意味着,当SYN cookie模式被激活并且积压队列未满时,如果客户端通过半开套接字向服务器发送一些数据,accept()函数将返回新的传入连接。