一个Queue还是两个Queue
IPC/IP栈对一个处于LISTEN
状态的socket有两种实现backlog queue的方式。
一个Queue
一个Queue能够包含两种状态的Connections:
- SYN RECEIVED
- ESTABLISHED
只有ESTABLISHED
状态的Connection才能被应用通过syscall accept()
方法获取到。
因此,该队列的长度由listen()
的参数backlog
决定。
一个SYN Queue 和 一个Accept Queue
这种情况下,处于SYN RECEIVED
状态的connection将会被添加到SYN queue,然后当连接状态变成ESTABLISHED
时再把其移动到Accept queue
中。
因此,Accept queue
只存储等待accept()
调用的connection。
不像前一个示例,linsten()
syscall的参数backlog
此时决定了Accept queue
的大小。
BSD
BSD选择了一个queue作为了它的实现(尽管事实上它内部使用了两个queue)。
当queue满时,它会简单的丢弃SYN包,并让客户端重试,而不会向接收到的SYN包发送SYN/ACK包。
Linux
从Linux 2.2起,Linux使用了两个queue:
backlog
表示Accept queue
的最大长度/proc/sys/net/ipv4/tcp_max_syn_backlog
表示SYN queue
的最大长度;新内核使用的是/proc/sys/net/core/somaxconn
(也叫net.core.somaxconn
)
从客户端的角度来看,一个connection在接收到SYN/ACK
包后,其状态就成为了ESTABLISHED
。
当SYN Queue满了
当接收到一个SYN包但SYN Queue满时:
- 若设置
net.ipv4.tcp_syncookies = 0
,则直接丢弃当前 SYN 包; - 若设置
net.ipv4.tcp_syncookies = 1
,则令want_cookie = 1
继续后面的处理;- 若
Accept queue
已满,并且qlen_young
的值大于 1 ,则直接丢弃当前 SYN 包。req_young_len
是SYN Queue
中还没有重新发送的SYN/ACK
包的连接数。 - 若
Accept queue
未满,或者qlen_young
的值未大于 1 ,则输出 “possible SYN flooding on port %d. Sending cookies.\n”,生成 syncookie 并在 SYN,ACK 中带上。
- 若
以下源码摘自linux-2.6.32版本
1 | int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) |
当Accept Queue满了
当接收到一个ACK包但Accept queue
满了时:
- 若设置
tcp_abort_on_overflow = 1
,则 TCP 协议栈回复 RST 包,并直接从 SYN queue 中删除该连接信息,表示废弃这个握手过程和这个连接; - 若设置
tcp_abort_on_overflow = 0
,则 TCP 协议栈将该连接标记为acked
,但仍保留在 SYN queue 中,并启动 timer 以便重发 SYN,ACK 包;当 SYN,ACK 的重传次数超过net.ipv4.tcp_synack_retries
设置的值时,再将该连接从 SYN queue 中删除;
通常把tcp_abort_on_overflow设置为0可以利于应对突发流量,当 TCP 全连接队列满导致服务器丢掉了 ACK,与此同时,客户端的连接状态却是 ESTABLISHED,进程就在建立好的连接上发送请求。只要服务器没有为请求回复 ACK,请求就会被多次重发。如果服务器上的进程只是短暂的繁忙造成 accept 队列满,那么当 TCP 全连接队列有空位时,再次接收到的请求报文由于含有 ACK,仍然会触发服务器端成功建立连接。
tcp_abort_on_overflow
参数位置位于/proc/sys/net/ipv4/tcp_abort_on_overflow
但是,如果Accept queue
满时,内核也会对SYN包接收速率强加一个限制:如果太多的SYN包被接受,其中将有一些会被抛弃。
此时,由客户端决定是否重发SYN包。
1 | /* |
使用单个Queue的缺点
导致单个queue满的两个主要因素:
- 应用调用
accept()
不够快,使得建立ESTABLISHED
的connection塞满了queue。 - 服务端与客户端之间的RTT(round-trip time,往返延时)比较大,处于
SYN RECEIVED
状态的connection塞满了queue。
而使用了两个queue,则SYN queue可以看出要传输的ACK包,Accept queue可以看出应用需要处理的连接数。