TCP 三次握手原理,你真的理解吗?

标签: | 发表时间:2020-07-17 15:50 | 作者:
出处:https://mp.weixin.qq.com

阿里妹导读:最近,阿里中间件小哥哥蛰剑碰到一个问题——client端连接服务器总是抛异常。在反复定位分析、并查阅各种资料文章搞懂后,他发现没有文章把这两个队列以及怎么观察他们的指标说清楚。


因此,蛰剑写下这篇文章,希望借此能把这个问题说清楚。欢迎大家一起交流探讨。


问题描述


场景:JAVA的client和server,使用socket通信。server使用NIO。


1.间歇性得出现client向server建立连接三次握手已经完成,但server的selector没有响应到这连接。

2.出问题的时间点,会同时有很多连接出现这个问题。

3.selector没有销毁重建,一直用的都是一个。

4.程序刚启动的时候必会出现一些,之后会间歇性出现。


分析问题


正常TCP建连接三次握手过程:



  • 第一步:client 发送 syn 到server 发起握手;

  • 第二步:server 收到 syn后回复syn+ack给client;

  • 第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack(此时client的56911端口的连接已经是established)。


从问题的描述来看,有点像TCP建连接的时候全连接队列(accept队列,后面具体讲)满了,尤其是症状2、4. 为了证明是这个原因,马上通过 netstat -s | egrep "listen" 去看队列的溢出统计数据:    



反复看了几次之后发现这个overflowed 一直在增加,那么可以明确的是server上全连接队列一定溢出了。


接着查看溢出后,OS怎么处理:



tcp_abort_on_overflow 为0表示如果三次握手第三步的时候全连接队列满了那么server扔掉client 发过来的ack(在server端认为连接还没建立起来)


为了证明客户端应用代码的异常跟全连接队列满有关系,我先把tcp_abort_on_overflow修改成 1,1表示第三步的时候如果全连接队列满了,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。


接着测试,这时在客户端异常中可以看到很多connection reset by peer的错误,到此证明客户端错误是这个原因导致的(逻辑严谨、快速证明问题的关键点所在)。


于是开发同学翻看java 源代码发现socket 默认的backlog(这个值控制全连接队列的大小,后面再详述)是50,于是改大重新跑,经过12个小时以上的压测,这个错误一次都没出现了,同时观察到 overflowed 也不再增加了。


到此问题解决,简单来说TCP三次握手后有个accept队列,进到这个队列才能从Listen变成accept,默认backlog 值是50,很容易就满了。满了之后握手第三步的时候server就忽略了client发过来的ack包(隔一段时间server重发握手第二步的syn+ack包给client),如果这个连接一直排不上队就异常了。


但是不能只是满足问题的解决,而是要去复盘解决过程,中间涉及到了哪些知识点是我所缺失或者理解不到位的;这个问题除了上面的异常信息表现出来之外,还有没有更明确地指征来查看和确认这个问题。


深入理解TCP握手过程中建连接的流程和队列


(图片来源:http://www.cnxct.com/something-about-phpfpm-s-backlog/)


如上图所示,这里有两个队列:syns queue(半连接队列);accept queue(全连接队列)。


三次握手中,在第一步server收到client的syn后,把这个连接信息放到半连接队列中,同时回复syn+ack给client(第二步);



第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出这个连接的信息放入到全连接队列中,否则按tcp_abort_on_overflow指示的执行。


这时如果全连接队列满了并且tcp_abort_on_overflow是0的话,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),如果client超时等待比较短,client就很容易异常了。


在我们的os中retry 第二步的默认次数是2(centos默认是5次):



如果TCP连接队列溢出,有哪些指标可以看呢?


上述解决过程有点绕,听起来懵,那么下次再出现类似问题有什么更快更明确的手段来确认这个问题呢?(通过具体的、感性的东西来强化我们对知识点的理解和吸收。)


netstat -s



比如上面看到的 667399 times ,表示全连接队列溢出的次数,隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。


ss 命令



上面看到的第二列Send-Q 值是50,表示第三列的listen端口上的全连接队列最大为50,第一列Recv-Q为全连接队列当前使用了多少。


全连接队列的大小取决于:min(backlog, somaxconn) . backlog是在socket创建的时候传入的,somaxconn是一个os级别的系统参数。


这个时候可以跟我们的代码建立联系了,比如Java创建ServerSocket的时候会让你传入backlog的值:


(来自JDK帮助文档:https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html)


半连接队列的大小取决于:max(64,  /proc/sys/net/ipv4/tcp_max_syn_backlog),不同版本的os会有些差异。


我们写代码的时候从来没有想过这个backlog或者说大多时候就没给他值(那么默认就是50),直接忽视了他,首先这是一个知识点的盲点;其次也许哪天你在哪篇文章中看到了这个参数,当时有点印象,但是过一阵子就忘了,这是知识之间没有建立连接,不是体系化的。但是如果你跟我一样首先经历了这个问题的痛苦,然后在压力和痛苦的驱动自己去找为什么,同时能够把为什么从代码层推理理解到OS层,那么这个知识点你才算是比较好地掌握了,也会成为你的知识体系在TCP或者性能方面成长自我生长的一个有力抓手。


netstat 命令


netstat跟ss命令一样也能看到Send-Q、Recv-Q这些状态信息,不过如果这个连接不是 Listen状态的话,Recv-Q就是指收到的数据还在缓存中,还没被进程读取,这个值就是还没被进程读取的 bytes;而 Send 则是发送队列中没有被远程主机确认的 bytes 数。



netstat -tn 看到的 Recv-Q 跟全连接半连接没有关系,这里特意拿出来说一下是因为容易跟 ss -lnt 的 Recv-Q 搞混淆,顺便建立知识体系,巩固相关知识点 。  


比如如下netstat -t 看到的Recv-Q有大量数据堆积,那么一般是CPU处理不过来导致的:


 

上面是通过一些具体的工具、指标来认识全连接队列(工程效率的手段)。  


实践验证一下上面的理解


把java中backlog改成10(越小越容易溢出),继续跑压力,这个时候client又开始报异常了,然后在server上通过 ss 命令观察到:



按照前面的理解,这个时候我们能看到3306这个端口上的服务全连接队列最大是10,但是现在有11个在队列中和等待进队列的,肯定有一个连接进不去队列要overflow掉,同时也确实能看到overflow的值在不断地增大。


Tomcat和Nginx中的Accept队列参数


Tomcat默认短连接,backlog(Tomcat里面的术语是Accept count)Ali-tomcat默认是200, Apache Tomcat默认100。



Nginx默认是511



因为Nginx是多进程模式,所以看到了多个8085,也就是多个进程都监听同一个端口以尽量避免上下文切换来提升性能   


总结


全连接队列、半连接队列溢出这种问题很容易被忽视,但是又很关键,特别是对于一些短连接应用(比如Nginx、PHP,当然他们也是支持长连接的)更容易爆发。 一旦溢出,从cpu、线程状态看起来都比较正常,但是压力上不去,在client看来rt也比较高(rt=网络+排队+真正服务时间),但是从server日志记录的真正服务时间来看rt又很短。


jdk、netty等一些框架默认backlog比较小,可能有些情况下导致性能上不去。


希望通过本文能够帮大家理解TCP连接过程中的半连接队列和全连接队列的概念、原理和作用,更关键的是有哪些指标可以明确看到这些问题(工程效率帮助强化对理论的理解)。


另外每个具体问题都是最好学习的机会,光看书理解肯定是不够深刻的,请珍惜每个具体问题,碰到后能够把来龙去脉弄清楚,每个问题都是你对具体知识点通关的好机会。


最后提出相关问题给大家思考


  1. 全连接队列满了会影响半连接队列吗?

  2. netstat -s看到的overflowed和ignored的数值有什么联系吗?

  3. 如果client走完了TCP握手的第三步,在client看来连接已经建立好了,但是server上的对应连接实际没有准备好,这个时候如果client发数据给server,server会怎么处理呢?(有同学说会reset,你觉得呢?)


提出这些问题,希望以这个知识点为抓手,让你的知识体系开始自我生长。


参考文章

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

http://www.cnblogs.com/zengkefu/p/5606696.html

http://www.cnxct.com/something-about-phpfpm-s-backlog/

http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/

http://jin-yang.github.io/blog/network-synack-queue.html#

http://blog.chinaunix.net/uid-20662820-id-4154399.html

每天一篇技术文章,

看不过瘾?

关注“ 阿里巴巴机器智能”,

发现更多AI干货。


 ↑  翘首以盼等你关注


你可能还喜欢

点击下方图片即可阅读


如果你的世界只剩下代码......


为什么做技术 PM 这么难?


毕业3年,为何技术能力相差越来越大?


关注「阿里技术」

把握前沿技术脉搏

相关 [tcp 握手 原理] 推荐:

[原]结合wireshark分析TCP和三次握手原理

- - HelloWorld
一、wireshark介绍.          wireshark数据包分析实战(第二版)电子书: http://pan.baidu.com/s/1LCZV   提取密码:   fqcv.         Wireshark(前称Ethereal)是一个 网络封包分析 软件. 网络封包分析 软件的功能是撷取 网络封包,并尽可能显示出最为详细的网络封包资料.

TCP 三次握手原理,你真的理解吗?

- -
阿里妹导读:最近,阿里中间件小哥哥蛰剑碰到一个问题——client端连接服务器总是抛异常. 在反复定位分析、并查阅各种资料文章搞懂后,他发现没有文章把这两个队列以及怎么观察他们的指标说清楚. 因此,蛰剑写下这篇文章,希望借此能把这个问题说清楚. 场景:JAVA的client和server,使用socket通信.

TCP的三次握手以及TCP状态转换图详解

- - CSDN博客系统运维推荐文章
今天来讨论一下TCP的三次握手以及TCP的状态转换图. 首先发一个三次握手的流程图如下:. 圖 2.4-3、三向交握之封包连接模式.   当用戶端想要对服务器端发起连接时,就必須要送出一個要求连线的封包,此时用戶端必须随机取用一個大于1024 以上的端口來做为程序通信的通道. 然后在 TCP 的表头当中,必须带有 SYN 的主动连线(SYN=1),並并且记下发送给服务器端的序列号(Sequence number = 10001).

TCP/IP三次握手和HTTP过程

- - 互联网 - ITeye博客
TCP/IP三次握手和HTTP过程.        原文地址:http://www.cnblogs.com/tiwlin/archive/2011/12/25/2301305.html. 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接. TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上.

TCP三次握手四次挥手详解

- - CSDN博客互联网推荐文章
TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 1;建立连接时,客户端向服务器端发送一个SYN包,进入SYN_SEND状态,在该状态下,客户端等待服务器端的确认包. 2;服务器端收到客户端的SYN包后,首先向客户端确认自己已收到客户端的SYN包,同时也要发送自己的SYN包,即要向发送方发送ACK包+SYN包,然后进入SYN——RECEIVE状态.

TCP连接的三次握手--一次故障记录

- - CSDN博客系统运维推荐文章
大家都知道tcp和udp协议,tcp可靠的网络传输协议,udp效率高但是不会进行传输确认,只有投递. 那么三次握手就是为了保证tcp的可靠传输,下边是用wireshark抓取一次失败的http请求的结果:. 首先TCP的三次握手是建立连接. NO1,113.31的主机给112.65的主机发送了一个包含syn的包并且设置seq等于0,来请求建立连接.

转载:Wireshark基本介绍和学习TCP三次握手

- - 互联网 - ITeye博客
Wireshark基本介绍和学习TCP三次握手. 用 Fiddler 来调试HTTP,HTTPS. 这篇文章介绍另一个好用的抓包工具wireshark, 用来获取网络数据封包,包括http,TCP,UDP,等网络协议包. 记得大学的时候就学习过TCP的三次握手协议,那时候只是知道,虽然在书上看过很多TCP和UDP的资料,但是从来没有真正见过这些数据包, 老是感觉在云上飘一样,学得不踏实.

从tcp原理角度理解Broken pipe和Connection reset by peer的区别

- - 你假笨
  在讲具体的原因之前,我们有必要补充下tcp这块的一些基础知识,我们都知道tcp通信有三次握手和四次挥手,网上介绍的文章也一大堆,图我也懒得画了,直接网上找一个图给大家.   介绍了基础原理之后,再介绍下抓包工具,tcpdump,这工具对你了解tcp的整个过程会非常有帮助,在你无法调试tcp实现的情况下这个工具自然也是必不可少的,具体用法网上有很多介绍,直接从man page上也可以看到详细的介绍,我也不多说啦,下面的截图就是tcpdump根据tcp通信过程获取到的.

tcp/ip调优

- Lucseeker - 在路上
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;.

浅谈TCP优化

- - 火丁笔记
很多人常常对 TCP优化有一种雾里看花的感觉,实际上只要理解了TCP的运行方式就能掀开它的神秘面纱. Ilya Grigorik 在「 High Performance Browser Networking」中做了很多细致的描述,让人读起来醍醐灌顶,我大概总结了一下,以期更加通俗易懂. 传输数据的时候,如果发送方传输的数据量超过了接收方的处理能力,那么接收方会出现丢包.