TCP 半连接队列和全连接队列详解(结合 Linux 2.6.32 内核源码分析)

一、什么是 TCP 半连接队列和全连接队列

TCP 三次握手期间,Linux 内核会维护两个队列,分别是:

  • 半连接队列,也称 SYN 队列
  • 全连接队列,也称 Accept 队列
    在这里插入图片描述
    不管是半连接队列还是全连接队列,都有最大长度限制

二、TCP 全连接队列

1、如何查看进程的 TCP 全连接队列大小?

# -l:显示正在监听(listening)的 socket
# -n:不解析服务名称
# -t:只显示 tcp socket
$ ss -lnt  | grep 55535
State       Recv-Q Send-Q         Local Address:Port                        Peer Address:Port
LISTEN      0      5               192.168.5.28:55535                                  *:*

注意

ss 命令的 Recv-Q/Send-Q 在「LISTEN 状态」和「非 LISTEN 状态」所表达的含义是不同的

/* Linux Kernel 2.6.32 tcp_diag.c */
static void tcp_diag_get_info( ... )
{
  ...
  /* LISTEN 状态 */
  if (sk->sk_state == TCP_LISTEN) {
    /* 当前全连接队列大小 */	    
    r->idiag_rqueue = sk->sk_ack_backlog;
    /* 当前全连接队列最大长度 */
    r->idiag_wqueue = sk->sk_max_ack_backlog;
  } else {
    /* 已收到但未被进程读取的字节数 */
    r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq;
    /* 已发送但未收到确认的字节数 */
    r->idiag_wqueue = tp->write_seq - tp->snd_una;
  }
  ...
}

「LISTEN 状态」的 Recv-Q/Send-Q 含义如下:

  • Recv-Q:当前全连接队列大小
  • Send-Q:当前全连接队列最大长度

2、TCP 全连接队列溢出问题

Linux 有个参数(tcp_abort_on_overflow),可以指定在 TCP 全连接队列满了的情况下使用什么策略回应客户端

/proc/sys/net/ipv4/tcp_abort_on_overflow 共有两个值:

  • 0:全连接队列满了的情况下,服务端丢弃客户端发来的 ACK 报文
  • 1:全连接队列满了的情况下,服务端回给客户端 RST 报文

注意

通常情况下,应该把参数 tcp_abort_on_overflow 设为 0,这样有利于应对突发流量

例如,TCP 全连接队列满了的情况下,服务端丢弃客户端发来的 ACK 报文,但此时客户端的连接状态却是 ESTABLISHED,客户端可以在建立好的 TCP 连接上发出请求,只要服务端没有对请求回复 ACK 应答,那么请求就会被客户端重发,如果服务端只是因为短暂的繁忙致使全连接队列满了,那么当全连接队列有空位时,再次接收到的请求报文中由于含有 ACK 标志,仍会触发服务端建立 TCP 连接,所以,tcp_abort_on_overflow=0 可以提高连接建立的成功率

3、TCP 全连接队列最大长度

min(somaxconn,backlog),其中,somaxconn 是 Linux 内核参数,可以通过 /proc/sys/net/core/somaxconn 调整,而 backlog 是 listen() 函数参数

/* Linux Kernel 2.6.32 socket.c */
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
  ...
  /* /proc/sys/net/core/somaxconn */
  somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
  /* TCP 全连接队列最大长度 */
  if ((unsigned)backlog > somaxconn)
    backlog = somaxconn;
  ...
}

三、TCP 半连接队列

1、TCP 半连接队列溢出问题

  1. 如果半连接队列满了,并且没有开启 tcp_syncookies,丢弃
  2. 如果全连接队列满了,并且没有重传 SYN+ACK 的连接请求多于 1 个,丢弃
  3. 如果没有开启 tcp_syncookies,并且 tcp_max_syn_backlog - 当前半连接队列长度 < (tcp_max_syn_backlog >> 2),丢弃
/* Linux Kernel 2.6.32 tcp_ipv4.c
 * TCP 第一次握手的 Linux 内核代码 
 */
int tcp_v4_conn_request( ... )
{
  ...
  /* 条件 1
   * 如果半连接队列满了
   */
  if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
#ifdef CONFIG_SYN_COOKIES
    if (sysctl_tcp_syncookies) {
      want_cookie = 1;
    } else
#endif
    /* 如果半连接队列满了,并且没有开启 tcp_syncookies,丢弃 */
    goto drop;
  }

  /* 条件 2
   * 如果全连接队列满了,并且没有重传 SYN+ACK 的连接请求多于 1 个,丢弃
   */
  if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
    goto drop;
  ...
  if (want_cookie) {
    ...
  } else if (!isn) {
    ...
    if ( ... ) {
      ...
    }
    /* 条件 3
     * 如果没有开启 tcp_syncookies,
     * 并且 tcp_max_syn_backlog - 当前半连接队列长度 < (tcp_max_syn_backlog >> 2),
     * 丢弃
     */
    else if (!sysctl_tcp_syncookies && 
       sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
        (sysctl_max_syn_backlog >> 2)) && ...) {
      ...
      goto drop_and_release;
    }
    ...
  }
  ...
}

2、TCP 半连接队列最大长度

tcp_max_syn_backlog < min(somaxconn,backlog) ? tcp_max_syn_backlog * 2 : min(somaxconn,backlog) * 2,其中,tcp_max_syn_backlog 是 Linux 内核参数,可以通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 调整
在这里插入图片描述

注意

半连接队列最大长度并不代表服务端处于 SYN_RCVD 状态的最大个数

我们在上述内容中总结过 TCP 第一次握手报文被丢弃的三种条件:

  1. 如果半连接队列满了,并且没有开启 tcp_syncookies,丢弃
  2. 如果全连接队列满了,并且没有重传 SYN+ACK 的连接请求多于 1 个,丢弃
  3. 如果没有开启 tcp_syncookies,并且 tcp_max_syn_backlog - 当前半连接队列长度 < (tcp_max_syn_backlog >> 2),丢弃

假设条件一不成立,也就是当前半连接队列长度没有超,而后的条件二也不成立,一旦满足条件三,”半连接队列最大长度并不代表服务端处于 SYN_RCVD 状态的最大个数“ 结论正确,例如 somaxconn = 128, backlog = 511,tcp_max_syn_backlog = 256,计算出半连接队列最大长度是 256,但是按照假设,一旦当前半连接队列长度 > tcp_max_syn_backlog - (tcp_max_syn_backlog >> 2),也就是 256 - 64 = 192,SYN 报文就会被丢弃,那么此时处于 SYN_RCVD 状态的最大个数是 193,而非 256

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值