TCP
是一种面向连接的、可靠的、基于字节流的传输层通信协议,在发送数据前,通信双方必须在彼此间建立一条连接。
所谓的”连接“,其实是客户端和服务端保存的一份关于对方的信息,如ip
地址、端口号等。TCP
可以看成是一种字节流,它会处理IP
层或以下的层丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP
头部。一个TCP
连接由一个4
元组构成,分别是两个IP
地址和两个端口号。
一个TCP
连接通常分为三个阶段:连接、数据传输、推出(关闭)。
通过三次握手建立一个链接,通常四次挥手来关闭一个连接。当一个连接被建立或终止时,交换的报文段只包含TCP
头部,而没有数据。
TCP报文的头部结构
在了解TCP
连接之前先来了解一下TCP
报文的头部结构。
上图中有几个字段需要充电介绍如下:
-
序号:
seq
序号,占32位,用来标识从TCP
源端向目的端发送的字节流,发起方发送数据时对此进行标记。 -
确认序号:
ack
序号,占32位,只有ACK
标志位为1
时,确认序号字段才有效,ack=seq+1
。 -
标志位:共6个,即
URG
、ACK
、PSH
、RST
、SYN
、FIN
等,具体含义如下: -
ACK
:确认序号有效。FIN
:释放一个连接。PSH
:接受方应该尽快将这个报文交给应用层。RST
:重置连接。SYN
:发起一个新连接。URG
:紧急指针(urgent pointer
)有效。需要不注意的是:不要将确认序号ack
与标志位中的ACK
搞混了。确认ack=发起方seq+1
,两端配对。
TCP的三次握手
三次握手的本质是确认通信双方收发数据的能力首先,我让信使运输一份信件给对方,对方收到了,那么他就知道我的发件能力是可以的。于是他给我回信,我若收到了,我便知道我的发件能力和收件能力是可以的,并且他的发件能力和我的收件能力是可以。然而此时他还不知道他的发件能力和我的收件能力到底可不可以,于是我最后回馈一次,他若收到了,他便清楚他的发件能力和我的收件能力是可以的。这,就是三次握手。
简单来说就是:
- 客户端向服务端发送
SYN
(同步位,SYN=1
,表示进行一个连接请求。) - 服务端返回
SYN
、ACK
- 客户端发送
ACK
(ACK
,确认位,ACK=1
,确认有效,ACK=0
,确认无效)。
具体细节:
- 第一次握手:客户端要向服务器发起连接请求,首先客户端随机生成一个起始序列号
ISN
(比如是100),那客户端向服务器发送的报文包含SYN
标志位(也就是SYN=1
),序列号seq=100
。 - 第二次握手:服务端收到客户端发过来的报文后,发现
SYN=1
,知道这是一个连接请求,于是将客户端的起始序列号100
存起来,并且随机生成一个服务端的起始序列号(比如是300)
。然后给客户端回复一段报文,回复报文包含SYN
和ACK
的标志(也就是SYN=1
,ACK=1
)、序列号=300
、确认号ack=101(客户端发过来的序列号+1)
。 - 第三次握手:客户端收到服务端回复后发现
ACK=1
、确认号ack=101
,于是知道服务端已经收到了序列号为100
的那段报文;同时发现SYN=1
,知道了服务端同意了这次连接,于是就将服务端的序列号300
保存下来。然后客户端再回复一段报文给服务端,报文包含ACK标志位(ACK=1
)、ack=301
(服务端序列号+1
)、seq=101
(第一次握手时发送报文是占据一个序列号的,所以这次seq
就从101
开始,需要注意的是不携带数据的ACK
报文是不占据序列号的,所以后面第一次正式发送数据时seq
还是101
)。当服务端收到报文后发现ACK=1
,并且ack=301
,就知道客户端收到序列号为300
的报文了,就这样客户端和服务度通过了TCP
建立了连接。
https://player.bilibili.com/player.html?bvid=BV18h41187Ep&t=416.6
用现实生活理解三次握手的具体细节:
三次握手的目的是建立可靠的通信通道,主要的目的就是双方确认自己与对方的发送与接受数据能力正常。
- 第一次握手:客户端什么都不确认;服务端确认了对方发送正常。
- 第二次握手:客户端确认了:自己发送、接受正常,对方发送、接受正常;服务器确认了:自己接受正常,对方发送正常
- 第三次握手:客户端确认了:自己发送、接受正常,对方发送、接受正常;服务器确认了:自己发送、接受正常,对方发送接受正常。
所以三次握手就能确认双方收发能力正常,缺一不可。
总结:
- 第一次握手,客户端将
SYN
置为1,随机产生一个初始序列化号seq
发送给服务端,进入SYN_SENT
状态。 - 第二次握手:服务端收到客户端的
SYN=1
之后,知道客户端请求建立连接,将自己的SYN
置为1
,ACK
置为1
,产生一个acknowledge number=服务端的sequence number +1
(也就是确认号ack
),并随机产生一个自己的初始化序列号,发送给客户端;进入SYN_RCVD
状态。 - 第三次握手:客户端检查
acknowledge number
是否为序列号sequence)+1
,ACK是否为1,检查正确之后将自己的ACK置为1,产生一个acknowledge number=服务器发出的序列号sequence number +1
发送给服务器;进入ESTABLISHED
状态;服务器检查ACK为1和acknowledge number=sequence number +1
之后,就进入ESTABLISHED
;完成三次握手,连接建立。
TCP的四次挥手
在网络数据传输中,传输层协议的断开连接的过程我们称为四次挥手。客户端和服务端都可以发起关闭请求。
用现实生活理解四次挥手:
四次挥手断开连接是因为要确定数据全部传输完了。
- 客户端与服务端交谈结束之后,客户端要结束此次会话,就会对服务器说:我要关闭连接了(第一次挥手)。
- 服务器收到客户的消息后说:好的,你要关闭连接了。(第二次挥手)。
- 然后服务器确定了没有话要和客户端说了,服务器就会对客户端说,我要关闭连接了(第三次挥手)。
- 客户端收到服务器要结束连接消息后说:已收到了你要关闭连接的消息。(第四次挥手),才关闭。
具体细节:
比如客户端初始化序列号ISA=100
,服务端初始化的序列号ISA=300
。TCP连接成功后客户端总共发送了1000
个字节的数据,服务端在客户端发FIN
报文前总共回复了2000
个字节的数据。
- 第一次挥手:当客户端的数据都传输完成后,
客户端
向服务端
发出连接释放报文(当然数据没法完成时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN
标志位(FIN=1
)、序列号seq=1101
(100+1+1000,其中的1是建立连接时占的一个序列号
)。需要注意的是客户端发出FIN
报文段后只是不能发数据了,但是还可以正常收数据;另外FIN
报文段即使不携带数据也要占据一个序列号。 - 第二次挥手:服务端收到客户端发的
FIN
报文后给客户端回复确认报文,确认报文包含ACK
标志位(ACK=1
)、确认号ack=1102
(客户端FIN报文序列1101+1
)、序列号seq=2300(300+2000)
。此时服务端处于关闭等待状态,而不是立马给客户端发FIN
报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。 - 第三次挥手:服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文,报文包含
FIN
和ACK
标志位(FIN=1,ACK=1
)、确认号和第二次挥手一样ack=1102
、序列号seq=2350(2300+50)
。 - 第四次挥手:客户端收到服务器发的
FIN
报文后,向服务端发出确认报文,确认报文包含ACK
标志位(ACK=1
)、确认号ack=2351
、序列号seq=1102
。注意客户端发出确认报文后不是立马释放TCP
连接,而是要经过2MSL
(最长报文段寿命的2倍时长)后才释放TCP
连接。而服务端一旦收到了客户端发出的确认报文就会立马释放TCP
连接,所以服务端结束TCP
连接的时间要比客户端早一些。
总结:
- 第一次挥手:客户端将
FIN
置为1,发送一个序列号seq
给服务端;进入FIN_WAIT
状态。 - 第二次挥手:服务端收到
FIN
之后,发送一个ACK=1
,acknowledge number=收到的序号+1
;进入CLOSE_WAit
状态。此时客户端已经没有要发送的数据了,但仍可以接受服务器发来的数据。 - 第三次挥手:服务端将
FIN
置为1
,发送一个序列号给客户端;进入LAST_ACK
状态。 - 第四次挥手:客户端收到服务器的
FIN
后,进入TIME_WAIT
状态;接着ACK
置为1,发送一个acknowledge number=序列号+1
给服务器;服务器收到后,确认acknowledge number
后,变为closed
状态,不在向客户端发送数据,客户端等待2 * MSL
(报文段的最长寿命)时间后,也进入CLOSED
状态。完成四次挥手。