【tcp】TCP 半连接队列、全连接队列基本概念

TCP 三次握手状态变化
对于客户端:
初始的状态是处于状态 。并不是一个真实的状态,而是一个假想的起点和终点 。
客户端调用以后会发送 SYN 同步报文给服务端,然后进入 SYN-SENT 阶段,客户端将保持这个阶段直到它收到了服务端的确认包 。
如果在 SYN-SENT 状态收到了服务端的确认包,它将发送确认服务端 SYN 报文的 ACK 包,同时进入状态,表明自己已经准备好发送数据 。
对于服务端:
初始状态同样是状态 。
在执行 bind、 调用以后进入 状态,等待客户端连接 。
当收到客户端的 SYN 同步报文以后,会回复确认同时发送自己的 SYN 同步报文,这时服务端进入 SYN-RCVD 阶段等待客户端的确认 。
当收到客户端的确认报文以后,进入 状态 。这时双方可以互相发数据了 。
当服务端调用函数时,TCP 的状态被从 CLOSE 状态变为,于此同时内核创建了两个队列:
半连接队列(queue),又称SYN 队列
全连接队列(queue),又称 队列

【tcp】TCP 半连接队列、全连接队列基本概念

文章插图
半连接队列
当客户端发起 SYN 到服务端,服务端收到以后会回 ACK 和自己的 SYN 。这时服务端这边的 TCP 从状态变为(SYN ),此时会将这个连接信息放入「半连接队列」,半连接队列也被称为 SYN Queue,存储的是 “ SYN ” 。
服务端回复 SYN+ACK 包以后等待客户端回复 ACK,同时开启一个定时器,如果超时还未收到 ACK 会进行 SYN+ACK 的重传,重传的次数由值确定 。
在上这个值等于 5 。
一旦收到客户端的 ACK,服务端就开始尝试把它加入另外一个全连接队列( Queue) 。
全连接队列
「全连接队列」包含了服务端所有完成了三次握手,但是还未被应用调用取走的连接队列 。
此时的处于状态 。每次应用调用 () 函数会移除队列头的连接 。如果队列为空,() 通常会阻塞 。全连接队列也被称为 队列 。
你可以把这个过程想象生产者、消费者模型:
内核是一个负责三次握手的生产者,握手完的连接会放入一个队列 。
我们的应用程序是一个消费者,取走队列中的连接进行下一步的处理 。
这种生产者消费者的模式,在生产过快、消费过慢的情况下就会出现队列积压 。
函数的第二个参数用来设置全连接队列大小,但不一定就会选用这一个值,还受限于,等下会有更详细的内容说明全连接队列大小的计算规则 。
int (int , int )
如果全连接队列满,内核会舍弃掉发过来的 ack(应用层会认为此时连接还未完全建立) 。
ss 命令可以查看全连接队列的大小和当前等待的连接个数,执行ss -lnt即可 。
【tcp】TCP 半连接队列、全连接队列基本概念

文章插图
对于 状态的套接字,Recv-Q 表示队列排队的连接个数,Send-Q 表示全连接队列(也就是队列)的总大小 。
套接字信息
和 ip 只显示了网络接口收发数据包的统计信息,但在实际的性能问题中,网络协议栈中的统计信息,我们也必须关注 。你可以用或者 ss,来查看套接字、网络栈、网络接口以及路由表的信息 。
我个人更推荐,使用 ss 来查询网络的连接信息,因为它比提供了更好的性能(速度更快) 。
比如,你可以执行下面的命令,查询套接字信息:
和 ss 的输出也是类似的,都展示了套接字的状态、接收队列、发送队列、本地地址、远端地址、进程 PID 和进程名称等 。
其中,接收队列(Recv-Q)和发送队列(Send-Q)需要你特别关注,它们通常应该是 0 。