您的位置:首页 > 理论基础 > 计算机网络

TCP的三次握手(建立连接)和四次挥手(关闭连接)

2017-06-07 15:55 549 查看

TCP的三次握手和四次挥手

三次握手(建立连接)

四次挥手(关闭连接)

TCP的TIME_WAITCLOSE_WAIT状态

Refer

一、三次握手(建立连接)

基本理解:窗口和滑动窗口

TCP的流量控制:

TCP使用窗口机制进行流量控制

什么是窗口?

连接建立时,各端分配一块缓冲区用来存储接收到的数据,并将缓冲区的尺寸发送给另一端,接收方发送的确认信息中包含了自己剩余的缓冲区尺寸,剩余缓冲区空间的数量叫做窗口。

TCP的流控过程(滑动窗口),图如下:



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

什么是三次握手?

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

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

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

客户端和服务器TCP状态迁移图



各个状态的意义如下:

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三次握手状态转移详解:

1.CLOSED:起始点,在超时或者连接关闭时候进入此状态。

2.LISTEN:svr端在等待连接过来时候的状态,svr端为此要调用socket, bind,listen函数,就能进入此状态。此称为应用程序被动打开(等待客户端来连接)。

3.SYN_SENT:客户端发起连接,发送SYN给服务器端。如果服务器端不能连接,则直接进入CLOSED状态。

4.SYN_RCVD:跟3对应,服务器端接受客户端的SYN请求,服务器端由LISTEN状态进入SYN_RCVD状态。同时服务器端要回应一个ACK,同时发送一个SYN给客户端;另外一种情况,客户端在发起SYN的同时接收到服务器端得SYN请求,客户端就会由SYN_SENT到SYN_RCVD状态。

5.ESTABLISHED:服务器端和客户端在完成3次握手进入状态,说明已经可以开始传输数据了。

关闭的4次挥手最难理解的状态是TIME_WAIT,存在TIME_WAIT的2个理由:

1、可靠地实现TCP全双工连接的终止。

2、允许老的重复分节在网络中消逝。

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(即接收方期望接收的下一个序列号)。

TCP包头结构



TCP包头结构分析:

源端口 16位

目标端口 16位

序列号 32位

确认号 32位

首部长度 4位

reserved 6位

控制代码 6位

窗口大小 16位

偏移量(紧急指针) 16位

校验和 16位

选项 32位(可选)

这样我们得出了TCP包头的最小长度,为20字节

下面是具体的例子截图:



**此图包含两部分信息:**TCP的三次握手(方框中的内容) (SYN, (SYN+ACK), ACK)

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的消息包

二、四次挥手(关闭连接)

注:主动、被动 与 服务器、客户端没有明确的对应关系。

关闭需要进行4次双方的交互,还包括要处理一些善后工作(TIME_WAIT状态),注意,这里主动关闭的一方或被动关闭的一方不是指特指服务器端或者客户端,是相对于谁先发起关闭请求来说的。

TCP四次挥手状态转移详解:

1.FIN_WAIT_1:主动关闭的一方,由状态5进入此状态。具体的动作时发送FIN给对方。

2.FIN_WAIT_2:主动关闭的一方,接收到对方的FIN ACK,进入此状态。由此不能再接收对方的数据。但是能够向对方发送数据。

3.CLOSE_WAIT:接收到FIN以后,被动关闭的一方进入此状态。具体动作时接收到FIN,同时发送ACK。

4.LAST_ACK:被动关闭的一方,发起关闭请求,由状态8进入此状态。具体动作时发送FIN给对方,同时在接收到ACK时进入CLOSED状态。

5.CLOSING:两边同时发起关闭请求时,会由FIN_WAIT_1进入此状态。具体动作是,接收到FIN请求,同时响应一个ACK。

6.TIME_WAIT:最纠结的状态来了。从状态图上可以看出,有3个状态可以转化成它,我们一一来分析:

a.由FIN_WAIT_2进入此状态:在双方不同时发起FIN的情况下,主动关闭的一方在完成自身发起的关闭请求后,接收到被动关闭一方的FIN后进入的状态。

b.由CLOSING状态进入:双方同时发起关闭,都做了发起FIN的请求,同时接收到了FIN并做了ACK的情况下,由CLOSING状态进入。

c.由FIN_WAIT_1状态进入:同时接受到FIN(对方发起),ACK(本身发起的FIN回应),与b的区别在于本身发起的FIN回应的ACK先于对方的FIN请求到达,而b是FIN先到达。这种情况概率最小。

四次挥手

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

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

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

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

TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。

简单说来是 “先关写,后关读”,一共需要四个阶段。

以客户机发起关闭连接为例:

服务端写通道关闭

客户端读通道关闭

客户端写通道关闭

服务端读通道关闭

关闭行为是在发起方数据发送完毕之后,给对方发出一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方收到了接收确认ACK之后,双方的数据通信完全结束,过程中每次接收都需要返回确认数据段ACK。

详细过程:

第一阶段 客户机发送完数据之后,向服务器发送一个FIN数据段,序列号为i;

服务器收到FIN(i)后,返回确认段ACK,序列号为i+1,关闭服务器写通道;

客户机收到ACK(i+1)后,关闭客户端读通道;(由此不能再接收对方的数据。但是能够向对方发送数据。)

第二阶段 服务器发送完数据之后,向客户机发送一个FIN数据段,序列号为j;

客户机收到FIN(j)后,返回确认段ACK,序列号为j+1,关闭客户机写通道;

服务器收到ACK(j+1)后,关闭服务器读通道。

这是标准的TCP关闭两个阶段,服务器和客户机都可以发起关闭,完全对称。

三、TCP的TIME_WAITCLOSE_WAIT状态

结合netstat和awk命令来统计网络连接数

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;

下面的文章就这几个状态的产生条件、对系统的影响以及处理方式进行简单描述。

SYN_RECV

服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。

两个相关系统配置:

net.ipv4.tcp_synack_retries :INTEGER


默认值是5

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

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,同时服务端关闭写通道。此时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状态?

原因有二:

一、保证TCP协议的全双工连接能够可靠关闭

二、保证这次连接的重复数据段从网络中消失

先说第一点,如果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,这样可以保证本次连接的所有数据都从网络中消失。

四、Refer

1、结合netstat和awk命令来统计网络连接数

2、TCP的三次握手(建立连接)和四次挥手(关闭连接)

3、TCP状态转换图详解
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: