TCP的三次握手(创建链接)和四次挥手(关闭链接)

创建链接: 前端

理解:窗口和滑动窗口
TCP的流量控制
TCP使用窗口机制进行流量控制
什么是窗口?

链接创建时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另外一端面试

接收方发送的确认信息中包含了本身剩余的缓冲区尺寸编程

剩余缓冲区空间的数量叫作窗口服务器

2. TCP的流控过程(滑动窗口)cookie

TCP(Transmission Control Protocol) 传输控制协议网络

三次握手并发

TCP是主机对主机层的传输控制协议,提供可靠的链接服务,采用三次握手确认创建一个链接:socket

位码即tcp标志位,有6种标示:tcp

SYN(synchronous创建联机)函数

ACK(acknowledgement 确认)

PSH(push传送)

FIN(finish结束)

RST(reset重置)

URG(urgent紧急)

Sequence number(顺序号码)

Acknowledge number(确认号码)

客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器TCP状态迁移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

 

各个状态的意义以下: 
LISTEN - 侦听来自远方TCP端口的链接请求; 
SYN-SENT -在发送链接请求后等待匹配的链接请求; 
SYN-RECEIVED - 在收到和发送一个链接请求后等待对链接请求的确认; 
ESTABLISHED- 表明一个打开的链接,数据能够传送给用户; 
FIN-WAIT-1 - 等待远程TCP的链接中断请求,或先前的链接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待链接中断请求; 
CLOSE-WAIT - 等待从本地用户发来的链接中断请求; 
CLOSING -等待远程TCP对链接中断的确认; 
LAST-ACK - 等待原来发向远程TCP的链接中断请求的确认; 
TIME-WAIT -等待足够的时间以确保远程TCP接收到链接中断请求的确认; 
CLOSED - 没有任何链接状态;

 

TCP/IP协议中,TCP协议提供可靠的链接服务,采用三次握手创建一个链接,如图1所示。

(1)第一次握手:创建链接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。

(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时本身也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。

(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

确认号:其数值等于发送方的发送序号 +1(即接收方指望接收的下一个序列号)。

图1 TCP三次握手创建链接  

TCP的包头结构:
源端口 16位
目标端口 16位
序列号 32位
回应序号 32位
TCP头长度 4位
reserved 6位
控制代码 6位
窗口大小 16位
偏移量 16位
校验和 16位
选项  32位(可选)
这样咱们得出了TCP包头的最小长度,为20字节

  • 第一次握手:
    客户端发送一个TCP的SYN标志位置1的包指明客户打算链接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
  • 第二次握手:
    服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。

 

  • 第三次握手.
    客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.而且把服务器发来ACK的序号字段+1,放在肯定字段中发送给对方.而且在数据段放写ISN的+1
下面是具体的例子截图:
1.此图包含两部分信息:TCP的三次握手(方框中的内容) (SYN, (SYN+ACK), ACK)
2. TCP的数据传输 ([TCP segment of a reassembled PUD])能够看出,server是将数据TCP层对消息包进行分片传输
(1)Server端收到HTTP请求如GET以后,构造响应消息,其中携带网页内容,在server端的HTTP层发送消息200 OK->server端的TCP层; 
(2)server端的TCP层对消息包进行分片传输; 
(3)client端的TCP层对接收到的各个消息包分片回送响应; 
(4)client端的TCP层每次收到一部分都会用ACK确认,以后server继续传输,client继续确认,直到完成响应消息的全部分片以后,Server 发送组合HTTP响应包 200 OK,此时在client端的消息跟踪中才能够显示 HTTP 200 OK的消息包

 

关闭链接:

 

因为TCP链接是全双工的,所以每一个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的链接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP链接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另外一方执行被动关闭。

 CP的链接的拆除须要发送四个包,所以称为四次挥手(four-way handshake)。客户端或服务器都可主动发起挥手动做,在socket编程中,任何一方执行close()操做便可产生挥手操做。

(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。 

(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN同样,一个FIN将占用一个序号。 

(3)服务器B关闭与客户端A的链接,发送一个FIN给客户端A。 

(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。 

TCP采用四次挥手关闭链接如图2所示。

 图2  TCP四次挥手关闭链接

 

参见wireshark抓包,实测的抓包结果并无严格按挥手时序。我估计是时间间隔过短形成。

 

深刻理解TCP链接的释放: 

 

因为TCP链接是全双工的,所以每一个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的链接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP链接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另外一方执行被动关闭。
TCP协议的链接是全双工链接,一个TCP链接存在双向的读写通道。 
简单说来是 “先关读,后关写”,一共须要四个阶段。以客户机发起关闭链接为例:
1.服务器读通道关闭
2.客户机写通道关闭
3.客户机读通道关闭
4.服务器写通道关闭
关闭行为是在发起方数据发送完毕以后,给对方发出一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方收到了接收确认ACK以后,双方的数据通讯彻底结束,过程当中每次接收都须要返回确认数据段ACK。
详细过程:
    第一阶段   客户机发送完数据以后,向服务器发送一个FIN数据段,序列号为i
    1.服务器收到FIN(i)后,返回确认段ACK,序列号为i+1关闭服务器读通道
    2.客户机收到ACK(i+1)后,关闭客户机写通道
   (此时,客户机仍能经过读通道读取服务器的数据,服务器仍能经过写通道写数据)
    第二阶段 服务器发送完数据以后,向客户机发送一个FIN数据段,序列号为j;
    3.客户机收到FIN(j)后,返回确认段ACK,序列号为j+1关闭客户机读通道
    4.服务器收到ACK(j+1)后,关闭服务器写通道
这是标准的TCP关闭两个阶段,服务器和客户机均可以发起关闭,彻底对称。
FIN标识是经过发送最后一块数据时设置的,标准的例子中,服务器还在发送数据,因此要等到发送完的时候,设置FIN(此时可称为TCP链接处于半关闭状态,由于数据仍可从被动关闭一方向主动关闭方传送)。若是在服务器收到FIN(i)时,已经没有数据须要发送,能够在返回ACK(i+1)的时候就设置FIN(j)标识,这样就至关于能够合并第二步和第三步。
读《Linux网络编程》关闭TCP链接章节,做如下笔记:

TCP的TIME_WAIT和Close_Wait状态

 

面试时看到应聘者简历中写精通网络,TCP编程,我常问一个问题,TCP创建链接须要几回握手?95%以上的应聘者都能答对是3次。问TCP断开链接须要几回握手,70%的应聘者能答对是4次通信。再问CLOSE_WAIT,TIME_WAIT是什么状态,怎么产生的,对服务有什么影响,如何消除?有一部分同窗就回答不上来。不是我扣细节,而是在通信为主的前端服务器上,必须有能力处理各类TCP状态。好比统计在本厂的一台前端机上高峰时间TCP链接的状况,统计命令:

 

 

  1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  

 

 

结果:

 

除了ESTABLISHED,能够看到链接数比较多的几个状态是:FIN_WAIT1, TIME_WAIT, CLOSE_WAIT, SYN_RECV和LAST_ACK;下面的文章就这几个状态的产生条件、对系统的影响以及处理方式进行简单描述。

 

TCP状态

TCP状态以下图所示:

可能有点眼花缭乱?再看看这个时序图


 

下面看下你们通常比较关心的三种TCP状态

SYN_RECV 

服务端收到创建链接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:

 

1,net.ipv4.tcp_synack_retries :INTEGER

默认值是5

对于远端的链接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN链接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃链接以前所送出的 SYN+ACK 数目。不该该大于255,默认值是5,对应于180秒左右时间。一般咱们不对这个值进行修改,由于咱们但愿TCP链接不要由于偶尔的丢包而没法创建。

2,net.ipv4.tcp_syncookies

通常服务器都会设置net.ipv4.tcp_syncookies=1来防止SYN Flood攻击。假设一个用户向服务器发送了SYN报文后忽然死机或掉线,那么服务器在发出SYN+ACK应答报文后是没法收到客户端的ACK报文的(第三次握手没法完成),这种状况下服务器端通常会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的链接,这段时间的长度咱们称为SYN Timeout,通常来讲这个时间是分钟的数量级(大约为30秒-2分钟)。

 

这些处在SYNC_RECV的TCP链接称为半链接,并存储在内核的半链接队列中,在内核收到对端发送的ack包时会查找半链接队列,并将符合的requst_sock信息存储到完成三次握手的链接的队列中,而后删除此半链接。大量SYNC_RECV的TCP链接会致使半链接队列溢出,这样后续的链接创建请求会被内核直接丢弃,这就是SYN Flood攻击。

 

可以有效防范SYN Flood攻击的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明。SYN Cookie是对TCP服务器端的三次握手协议做一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。若是合法,再分配专门的数据区进行处理将来的TCP链接。

 

观测服务上SYN_RECV链接个数为:7314,对于一个高并发链接的通信服务器,这个数字比较正常。

CLOSE_WAIT

发起TCP链接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种情况通常都是因为server端代码的问题,若是你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。

TIME_WAIT

根据TCP协议定义的3次握手断开链接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短链接的服务器,若是是由服务器主动关闭客户端的链接,将致使服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,中止服务。

 

为何须要TIME_WAIT?TIME_WAIT是TCP协议用以保证被从新分配的socket不会受到以前残留的延迟重发报文影响的机制,是必要的逻辑保证。

 

和TIME_WAIT状态有关的系统参数有通常由3个,本厂设置以下:

net.ipv4.tcp_tw_recycle = 1

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_fin_timeout = 30

 

net.ipv4.tcp_fin_timeout,默认60s,减少fin_timeout,减小TIME_WAIT链接数量。

 

net.ipv4.tcp_tw_reuse = 1表示开启重用。容许将TIME-WAIT sockets从新用于新的TCP链接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1表示开启TCP链接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。



为了方便描述,我给这个TCP链接的一端起名为Client,给另一端起名为Server。上图描述的是Client主动关闭的过程,FTP协议中就这样的。若是要描述Server主动关闭的过程,只要交换描述过程当中的Server和Client就能够了,HTTP协议就是这样的。

描述过程:
Client调用close()函数,给Server发送FIN,请求关闭链接;Server收到FIN以后给Client返回确认ACK,同时关闭读通道(不清楚就去看一下shutdown和close的差异),也就是说如今不能再从这个链接上读取东西,如今read返回0。此时Server的TCP状态转化为CLOSE_WAIT状态。
Client收到对本身的FIN确认后,关闭 写通道,再也不向链接中写入任何数据。
接下来Server调用close()来关闭链接,给Client发送FIN,Client收到后给Server回复ACK确认,同时Client关闭读通道,进入TIME_WAIT状态。
Server接收到Client对本身的FIN的确认ACK,关闭写通道,TCP链接转化为CLOSED,也就是关闭链接。
Client在TIME_WAIT状态下要等待最大数据段生存期的两倍,而后才进入CLOSED状态,TCP协议关闭链接过程完全结束。

以上就是TCP协议关闭链接的过程,如今说一下TIME_WAIT状态。
从上面能够看到,主动发起关闭链接的操做的一方将达到TIME_WAIT状态,并且这个状态要保持Maximum Segment Lifetime的两倍时间。为何要这样作而不是直接进入CLOSED状态?

缘由有二:
1、保证TCP协议的全双工链接可以可靠关闭
2、保证此次链接的重复数据段从网络中消失

先说第一点,若是Client直接CLOSED了,那么因为IP协议的不可靠性或者是其它网络缘由,致使Server没有收到Client最后回复的ACK。那么Server就会在超时以后继续发送FIN,此时因为Client已经CLOSED了,就找不到与重发的FIN对应的链接,最后Server就会收到RST而不是ACK,Server就会觉得是链接错误把问题报告给高层。这样的状况虽然不会形成数据丢失,可是却致使TCP协议不符合可靠链接的要求。因此,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,可以保证对方收到ACK,最后正确的关闭链接。

再说第二点,若是Client直接CLOSED,而后又再向Server发起一个新链接,咱们不能保证这个新链接与刚关闭的链接的端口号是不一样的。也就是说有可能新链接和老链接的端口号是相同的。通常来讲不会发生什么问题,可是仍是有特殊状况出现:假设新链接和已经关闭的老链接端口号是同样的,若是前一次链接的某些数据仍然滞留在网络中,这些延迟数据在创建新链接以后才到达Server,因为新链接和老链接的端口号是同样的,又由于TCP协议判断不一样链接的依据是socket pair,因而,TCP协议就认为那个延迟的数据是属于新链接的,这样就和真正的新链接的数据包发生混淆了。因此TCP链接还要在TIME_WAIT状态等待2倍MSL,这样能够保证本次链接的全部数据都从网络中消失。

各类协议都是前人千锤百炼后获得的标准,规范。从细节中都能感觉到精巧和严谨。每次深刻都有同一个感受,精妙。

相关文章
相关标签/搜索