设计师图解TCP链接过程

前言

      咱们知道Ip层包裹着tcp报文段把它从源Ip运送到目的Ip,若是过程当中出现差错(16位的Ip检验和错误),Ip协议会直接丢弃该数据报而且不生成差错报文。这种状况tcp会发现数据丢失并进行重传。
      这篇文章想探讨一下TCP协议是经过什么方式作到这些的,曾经作过设计师的我忍不住抄起老本行画两张图。前端

IP、UDP、TCP差别

      IP是网路层协议,TCP和UDP都是运输层协议,他们都是基于IP协议传递数据。接下来咱们经过几个重要首部分析一下它们的异同。
      三个协议首部中都有16位检验和,可是,IP协议首部中的检验和只覆盖IP首部,而TCP和UDP首部中的检验和是包含全部数据的,也就是说IP协议不关心数据是否正确,而TCP和UDP关心。
      IP首部中最重要的是32位源IP和目的IP地址,其它首部都是配合它顺利的把数据从源IP传到目的IP,TCP和UDP负责标记对应端口,那么TCP和UDP的区别有是什么呢?
      咱们先看UDP,它只有4个首部,前两个肯定端口,后两个保证数据的准确。IP协议把数据传递到目的IP地址的机器上,UDP经过首部中的长度和检验和校验数据,若是校验没有经过,那么UDP协议会直接丢弃数据,而不会向源IP和端口发送消息,因此说UDP协议是面向数据报的。
      UDP的缺点就是:咱们不能保证对方必定收到了我发送的数据。可是从首部中很容易能看出它的优势:就是轻量、速度快。
      再看TCP,它除了关心数据以外明显还关心更多的东西,其中最重要的就是关心链接的可靠性。下面咱们将主要经过TCP链接和断开过程看看这些首部都是什么做用。
      TCP首部中有6个标志比特位,它们默认都是0,须要时设置为1,经过它们完成询问和应答。它们分别表示的含义以下:

URG: 紧急指针
ACK: 确认序号有效
PSH: 尽量快地将数据送往接收进程
RST: 重建链接
SYN: 同步序号用来发起一个链接
FIN: 发端完成发送任务
算法

链接过程和状态

      我画了一张图来描述TCP链接的过程和状态变化: 缓存

通常状况(图中紫色和绿色部分)

三次握手
第一条紫色箭头(从上到下):

      服务器端默认处于 LISTEN 状态监听着某个端口。当客户端想要链接这个端口的时候,客户端首先会随机生成一个初始序号(图中的j),首部中的32位序号(如下简称序号)就变成j+1,同时首部中的SYN由0置为1。客户端发送完带有这些首部的TCP段后状态变为 SYN_SENT 服务器

第一条绿色箭头:

    当服务器收到客户端第一个带有SYN的TCP段的时候,客户端由 LISTEN 状态变为 SYN_RCVD 状态,并发送一段数据,其中首部ACK置为1,32位确认号(如下简称确认号)为客户端发过来的序号(j)+1,含义就是客户端的数据已经收到。同时SYN也会置为1,序号为k。含义是我也要与你创建链接。网络

第二条紫色箭头:

      当客户端收到服务器的ACK后,状态变动为 ESTABLISLISHED ,同时发送数据,首部ACK为k+1,含义是服务器的数据已经收到。这时客户端已经创建链接,而服务器端是否能创建链接取决于它可否收到当前客户端发送的这段带有ACK的数据。若是服务器收到的话,它的状态也会变成 ESTABLISLISHED
      到此,链接创建,能够继续交换数据。前端工程师

四次挥手
第三条紫色箭头:

      在不考虑断电等特殊状况下,主动关闭的一方(图中为客户端但不老是)发送FIN+序号m,状态变动为 FIN_WAIT_1 并发

第二条绿色箭头:

      被动关闭的一方(图中为服务器但不老是)收到后状态变动为 CLOSE_WAIT 。同时里当即发送确认号m+1,头部ACK置为1,这时服务端状态变动为 LAST_ACK ,很容易理解,这是最后一次确认,客户端收到ACK后,状态变动为 FIN_WAIT_2 tcp

第三条绿色箭头:

      服务器 LAST_ACK 后还可能继续作其它的操做,作完以后,服务器也会发送FIN+序号n。工具

第四条紫色箭头:

      客户端收到FIN后,状态变动为 TIME_WAIT ,同时发送确认信息ACK n+1,服务器收到ACK后,状态变动为 CLOSE
      到此,链接断开。优化

为何挥手是4次而不是3次?

      挥手的时候有个问题,为何ACK和FIN不能一块儿发送呢?这是由TCP的半关闭(half-close)形成的。TCP链接是全双工(即数据在两个方向上能同时传递),所以每一个方向必须单独地进行关闭。
      收到一个FIN只意味着对方不会再向我发送数据了,可是TCP仍然支持我继续向对方发送数据(固然,在实际的应用中,直有不多的程序这样作),若是咱们用Wireshark等工具抓包时也常常会看到挥手只有3次的状况。

TIME_WAIT旁边的2MSL是什么?

       TIME_WAIT 状态也称为2倍MSL等待状态。MSL是TCP的最大报文生存时间。当一个TCP主动关闭并发送最后一个ACK(图中右下角最后一条紫色线)后,必须在 TIME_WAIT 状态等待两倍的MSL时间,防止对方没有收到这个ACK而后重发FIN。因此一去一回最多就是两倍时间。
      操做系统默认240s(也就是2倍的两分钟)后会关闭处于 TIME_WAIT 的链接,在这以前端口一直会被占用。
      在Linux服务器上能够经过变动/etc/sysctl.conf文件去修改该缺省值(秒):net.ipv4.tcp_fin_timeout=30。但这一般也会出现一些问题。

同时打开(图中紫色和橙色部分)

      两个应用程序彼此同时打开的可能性很小。这时双方都是客户端也都是服务器。具体流程和通常状况同样,只不过挥手的时候是4次而不是3次。TCP协议认为这种状况是创建一条链接而不是两条。

同时关闭(图中紫色和橙色部分)

      为了方便我把同时打开和同时关闭画在了一块儿,其实同时打开不必定同时关闭。不一样时打开也有可能同时关闭。

TCP服务器处理端口号

      咱们先调用natstat命令看一下telnet服务器。-a:显示全部;-n:以点分十进制表示IP;-f inet:只显示TCP和UDP。首先,没有链接的时候它是这样的:

      表头依次为协议、接收请求数、发送请求数、本地地址、远程地址和状态。图中数据显示:当前本机的23端口正在处于 LISTEN状态,监听着任意IP发来的请求。下面再看一下当请求过来时的状态:
      状态一共有3条,这三条状态对应着三个进程, LISTEN状态的进程一直存在并接受SYN报文段,一旦握手成功,系统内核中的TCP模块就建立一个处于 ESTABLISHED状态的进程。

呼入链接请求队列

      若是服务器在同一时间收到大量请求,而服务器当前没有能力处理(或有更高优先级的进程)。TCP会先将这个请求缓存在一个固定长度的队列中(队列长度通常为5,称为积压值)。
      应用层会不断消费该队列,一旦产生积压,服务器会中止接收SYN报文,而且不返回任何消息,这时客户端会显示超时。

延时确认时间

      一般TCP在接收到数据时并不当即发送ACK;而是推迟发送,以便将ACK与须要沿该方向发送的数据一块儿发送(这种现象称为数据捎带ACK)。绝大多数实现采用的时延为200ms。
      200ms是最大时间,若是一直有数据等待发送,那么就不存在延时确认时间。

Nagle算法

      Nagle算法是为了解决小段问题。所谓“小段”,指的是小于最大MSS尺寸的数据块。例如应用程序一次产生一字节数据(例如交互式输入),这样会致使网络因为太多的包而过载。
      Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。规则以下:

一、若是包长度达到MSS,则容许发送;
二、若是该包含有FIN,则容许发送;
三、设置了TCP_NODELAY选项,则容许发送;
四、未设置TCP_CORK选项时,若全部发出去的小包均被确认,则容许发送;
五、上述条件都未知足,但发生了超时(通常为200ms),则当即发送。

      Nagle算法的优势是自适应:确认到达的越快,发送的就越快。
      Nagle算法的缺点是发送存在延时。咱们能够经过TCP_NODELAY套接字选项来关闭Nagle算法。

总结

      对于前端工程师来说,了解TCP协议可以帮助咱们更好的理解并使用HTTP协议。也可以帮助咱们对网络进行更加细致的优化。个人理解也颇有限,但愿可以和你们共同讨论。

参考资料

  • 《Tcp/ip详解-卷一》
  • 《图解Tcp/ip》
相关文章
相关标签/搜索