linux tcp 半连接队列和全连接队列

TCP建立连接的“三次握手”过程

上图就是tcp建联的三次握手过程。

  1. Server端需要先调用bind()方法,绑定ip和端口号,再调用listen()方法,然后就可以等待来自Client连接了
  2. Client 调用connect()后,就会发送SYN包到Server,此时Client端处理SYN_SENT状态
  3. Server收到SYN后,Server进入SYN_RCVD状态,回复SYN + ACK,此时该socket被放入半连接队列(SYN QUEUE)中。(这里要侧重说明一下,这里并不是说从Listen状态变成了SYN_RCVD,而是会生成一个新的(或者复用旧的)socket,新socket状态会变成SYN_RCVD状态,Server端的原来监听的socket状态还依然是LISTEN状态)
  4. Client 收到Server发回的ACK后,会再发送一次ACK到Server,Server收到ACK后三次连接建立,此时会把该socket从半连接队列中取出来放入全连接队列(ACCEPTED QUEUE)中,此时Client与Server端全部处理ESTABLISHED状态
  5. Server端调用accept()方法后,该连接会从全连接队列移出,交给应用层处理。

什么是“半连接队列(syn queue)" 和 "全连接队列(accept queue)"?

半连接队列:保存处于SYN_RCVD状态的socket的队列。

全连接队列:保存处于ESTABLISHED状态的socket的队列。(已经完成三次握手,Server还未调用accept()函数将socket交由应用层处理)

全连接队列(accept queue)

全连接对列最大长度计算公式:

min(net.core.somaxconn, backlog)

ps:

  • net.core.somaxconn为/proc/sys/net/core/somaxconn
  • backlog,listen(int sockfd, int backlog)函数传入的参数。比如:nginx中的listen指令的backlog参数。例如:'listen 80 backlog=1000;'

查看全连接队列长度

注意:连接状态不同,Recv-Q和Send-Q的含义不相同。

当连接在LISTEN状态下时,Recv-Q表示当前全连接队列的长度, Send-Q表示最大的全连接队列长度

# ss -lnt

State       Recv-Q Send-Q  Local Address:Port   Peer Address:Port

LISTEN      0         32768               *:9999              *:*  

LISTEN      0         100                 *:8080              *:*  

LISTEN      0         128                 *:18822             *:*  

LISTEN      0         50                  *:7879              *:*  

当连接在非LISTEN状态下时,Recv-Q表示已收到但未被应用进程读取的字节数;Send-Q表示的是已发送但未收到确认的字节数。

# ss -lnt | grep 80

State       Recv-Q Send-Q   Local Address:Port   Peer Address:Port

LISTEN     51        128          *:80                       *:*                 

LISTEN     0          128         :::80                      :::*                 

LISTEN     0          128         :::58803                   :::*                 

# ss -lnt | grep 80

LISTEN     129    128          *:80                       *:*                 

LISTEN     0      128         :::80                      :::*                 

LISTEN     0      128         :::58803                   :::*

查看全连接队列(accept queue)溢出的统计信息

# netstat -s | grep overflowed

    172308 times the listen queue of a socket overflowed

# netstat -s | grep overflowed

    175641 times the listen queue of a socket overflowed

注意:“172308 ,"175641 "是个累计值,常用后一次与前一次的取差值,若为正,则表示队列溢出。

全连接队列满了,怎么办?

当全连接队列满了,linux默认是丢弃连接,还可以通过/proc/sys/net/ipv4/tcp_abort_on_overflow值来控制。

若为0,则丢弃连接(默认)

若为1,则server 发送一个 reset 包给 client

如何调整全连接队列大小?

因为全连接队列大小=min(/proc/sys/net/core/somaxconn,backlog)。所以调整全连接队列大小时,两个都需要调。

比如:nginx默认backlog是511,linux 3.10.0内核/proc/sys/net/core/somaxconn 默认是128。

调整实例:

echo 2000 > /proc/sys/net/core/somaxconn

[root@rocketmq-server ipv4]# cat /etc/nginx/nginx.conf | grep listen

        listen       80 default_server backlog=1500;

[root@rocketmq-server ipv4]# ss -lnt | grep 80

LISTEN     0      1500         *:80                       *:* 

所以min(2000,1500)全连接队列变成了1500.

半连接队列

半连接队列最大长度计算公式

很多博客说SYN队列的最大长度是由/proc/sys/net/ipv4/tcp_max_syn_backlog参数指定的。实际上,只有当linux内核版本早于2.6.20时,SYN队列才等于backlog的大小。

实际上:SYN队列的最大长度由三个参数指定

  • 当你调用listen()时,传入的积压参数backlog
  • /proc/sys/net/core/somaxconn 的默认值为 128
  • /proc/sys/net/ipv4/tcp_max_syn_backlog 的默认值为 1024

查看当前半连接队列长度(只能粗略估计,SYN-RECV的数量少于实际半连接队列的长度)

#ss -antp | grep SYN-RECV | wc -l

查看半连接队列(syn queue)溢出的统计信息

# netstat -s | grep dropped

    166 SYNs to LISTEN sockets dropped

注意:“166”是个累计值,常用后一次与前一次的取差值,若为正,则表示队列溢出。

半连接队列满了,怎么办?

若半连接队列满了,根据不同情况,对连接可能有不同的处理方式,可能是直接丢弃,也可能是发送reset包

分如下几种情况:

  1. 如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
  2. 若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃;
  3. 如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;

"max_syn_backlog >> 2" :相当于右移2位,即max_syn_backlog / (2*2)

如何防御syn洪水攻击?

  • 增大半连接队列最大长度 (同时调大:/proc/sys/net/core/somaxconn,/proc/sys/net/ipv4/tcp_max_syn_backlog,listen(int sockfd, int backlog)中的backlog参数<每个服务都不一样>
  • 开启tcp_syncookies功能(echo 1 > /proc/sys/net/ipv4/tcp_syncookies )
  • 减少syn + ack重传次数(echo 1 > /proc/sys/net/ipv4/tcp_synack_retries )

参考文章:

4.4 TCP 半连接队列和全连接队列 | 小林coding

TCP SYN Queue and Accept Queue Overflow Explained - Alibaba Cloud Community

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP (Transmission Control Protocol) 连接全连接是建立两个进程间通信的两种状态,它们的主要区别在于网络连接的状态和数据传输的过程: **连接(三次握手):** - 在客户端发起连接请求之前,首先发送一个SYN(同步序号)包给服务器。 - 服务器接收到SYN包后,会回应一个SYN+ACK(同步确认)包,同时设置期望序列号。 - 客户端再次回应一个ACK(确认)包,表示收到了服务器的响应并且同意连接条件。 在这个阶段,双方已经交换了必要的控制信息,但是还没有正式的数据传输,因此还不是一个完连接状态。 **全连接(四次挥手):** - 当服务器和客户端都准备好开始数据传输时,他们各自维护一个完整的连接(即双方都有SYN+ACK包来回交互)。 - 数据传输结束后,一方希望关闭连接,通常由发起方发送FIN(结束)包,请求释放连接。 - 对方收到FIN包后,发送ACK作为应答,并在完成数据发送后也发送FIN包结束本方向的连接。 - 最后,双方互相发送ACK确认对方已接收FIN,完成断开连接的所有步骤。 总的来说,连接是在连接建立前的试探阶段,而全连接则是实际数据传输的稳定状态。连接节省了资源,因为它不需要立即创建一个双工连接,但在数据传输开始前需要更多交互。全连接更高效,一旦建立就可以直接传输数据,直到双方协商关闭。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值