重学TCP协议(9) 半连接队列、全连接队列 | 小册免费学

1,678 阅读3分钟

1. 半连接队列、全连接队列基本概念

image.png

  1. 三次握手中,在第一步server收到client的syn后,把相关信息放到半连接队列中,同时回复syn+ack给client(第二步),同时开启一个定时器,如果超时还未收到 ACK 会进行 SYN+ACK 的重传,重传的次数由 tcp_synack_retries 值确定。在 CentOS 上这个值等于 5。;
  2. 第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出相关信息放入到全连接队列中,否则按tcp_abort_on_overflow指示的执行。

1.1 tcp_abort_on_overflow

  • tcp_abort_on_overflow 为 0 表示三次握手最后一步全连接队列满以后 server 会丢掉 client 发过来的 ACK,服务端随后会进行重传 SYN+ACK。
  • tcp_abort_on_overflow 为 1 表示全连接队列满以后服务端直接发送 RST 给客户端。

1.2 ss 命令

ss 命令可以查看全连接队列的大小和当前等待 accept 的连接个数,执行 ss -lnt 即可

ss -lnt | grep :9090
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     51     50           *:9090                     *:*

Recv-Q 表示 accept 队列排队的连接个数,Send-Q 表示全连接队列(也就是 accept 队列)的总大小。

2. 半连接队列

2.1 半连接队列的长度

半连接队列的大小由3个值决定:

  • 用户层 listen 传入的backlog
  • 系统变量 net.ipv4.tcp_max_syn_backlog,默认值为 128
  • 系统变量 net.core.somaxconn,默认值为 128

2.2 半连接队列长度的计算过程

  1. 如果用户传入的 backlog 值大于系统变量 net.core.somaxconn 的值,用户设置的 backlog 不会生效,使用系统变量值,默认为 128。

  2. 将上一步计算的backlog值穿给nr_table_entries,sysctl_max_syn_backlog 为 net.ipv4.tcp_max_syn_backlog 的值

int reqsk_queue_alloc(struct request_sock_queue *queue,
		      unsigned int nr_table_entries)
{
    nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
    nr_table_entries = max_t(u32, nr_table_entries, 8);
    nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
    	
    for (lopt->max_qlen_log = 3;
         (1 << lopt->max_qlen_log) < nr_table_entries;
         lopt->max_qlen_log++);
}
  • 在 nr_table_entries 与 sysctl_max_syn_backlog 两者中的较小值,赋值给 nr_table_entries(因为sysctl_max_syn_backlog默认是128,如果没有修改,产生的最大值就只能是128)
  • 在 nr_table_entries 和 8 取较大值,赋值给 nr_table_entries(最小只能是8)
  • nr_table_entries + 1 向上取求最接近的最大 2 的指数次幂(保证是2的幂次)
  • 通过 for 循环找不大于 nr_table_entries 最接近的 2 的对数值

3.全连接队列

全连接队列」包含了服务端所有完成了三次握手,但是还未被应用调用 accept 取走的连接队列。此时的 socket 处于 ESTABLISHED 状态。每次应用调用 accept() 函数会移除队列头的连接。如果队列为空,accept() 通常会阻塞。全连接队列也被称为 Accept 队列。

3.1 全连接队列的长度

全连接队列的大小由3个值决定:

  • 用户层 listen 传入的backlog
  • 系统变量 net.core.somaxconn,默认值为 128

3.2 全连接队列长度的计算过程

全连接队列的大小是 listen 传入的 backlog 和 somaxconn 中的较小值

3.3 全连接队列溢出的情况

image.png

  • 如上图,150166号包是三次握手中的第三步client发送ack给server,然后150167号包中client发送了一个长度为816的包给server。

  • 因为在这个时候client认为连接建立成功(因为已经完成三次握手了),但是server上这个连接实际没有ready(因为全连接队列已经满了),所以server直接丢掉client发来的ACK包,并且一段时间后,server认为自己第二次握手的syn+ack包丢包了,因此就重发syn+ack包(SYN+ACK重传的次数是由操作系统的一个文件决定的/proc/sys/net/ipv4/tcp_synack_retries

  • 一段时间后client又收到了syn+ack包,认为第三次握手的ack丢包了,然后重传这816个字节的ack包。一直到超时,client主动发fin包断开该连接。