假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是close
客户端(Client):用户发送请求(request)的叫客户端
服务端(Server):接收请求做出响应(response)的叫服务端
通过抓包得到的数据:
第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位SYN=1
,序列号:seq=x
。
第一次握手前客户端的状态为
CLOSE
,第一次握手后客户端的状态为SYN-SENT
。此时服务端的状态为Listen
。
第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y
,然后给客户端回复一段报文,其中包括标志位SYN=1
,ACK=1
,序列号seq=y
,确认号ack=x+1
第二次握手前服务端的状态为
Listen
,第二次握手后服务端的状态为SYN-RCVD
,此时客户端的状态为SYN-SENT
。(其中SYN=1
表示要和客户端建立一个连接,ACK=1
表示确认序号有效)
第三次握手:客户端收到服务端发来的报文后,会再向服务端发送报文,其中包含标志位ACK=1
,序列号seq=x+1
,确认号ack=y+1
第三次握手前客户端的状态为
SYN-SENT
,第三次握手后客户端和服务端的状态都为Established
。此时连接建立完成
第三次握手,主要为了防止已失效的连接请求报文段突然又传输到了服务端,导致产生问题
- 比如客户端A发出连接请求,可能因为网络阻塞原因,A没有收到确认报文,于是A再重传一次连接请求。然后连接成功,等待数据传输完毕后,就释放了连接。然后A发出的第一个连接请求等到连接释放以后的某个时间才到达服务端B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段。
- 如果不采用三次握手,只要B发出确认,就建立新的连接了,此时A不会响应B的确认且不发送数据,则B一直等待A发送数据,浪费资源
可以,但是没必要。三次握手是为了确认双方的发送和接收的能力,三次足够解决问题了
第三次握手的时候可以携带数据。前两次不能携带数据。
原因:如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的SYN报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险
第三次握手的时候,客户端已经处于
Established
状态,并且已经能够确认服务器的接收、发送能力正常,这个时候相对安全了,可以携带数据
客户端的应用进程先向其TCP发出连接释放报文段(FIN=1,seq=x
),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1
(终止等待1)状态,等待服务端的确认
服务端收到连接释放报文段后即发出确认报文段(ACK=1,ack=x+1,seq=y
)
服务端进入
CLOSE-WAIT
(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放
客户端收到服务端的确认后,进入FIN-WAIT-2
(终止等待2)状态,等待服务端发出的连接释放报文段
服务端发送完数据,就会发出连接释放报文段(FIN=1,seq=z,ack=x+1
),服务端进入LAST-ACK
(最后确认)状态,等待客户端的确认
客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=z+1,ack=x+1
),客户端进入TIME-WAIT
(时间等待)状态。此时TCP为释放掉,需要经过时间等待计时器设置的时间2MSL
(最大报文生存时间)后,客户端才进入CLOSE
状态。服务端收到客户端发出的确认报文段后关闭连接,若没收到客户端发出的确认报文段,服务端就会重传连接释放报文段
保证客户端发送的最后一个ACK报文段能够到达服务端。
这个
ACK
报文段有可能丢失,服务端收不到这个确认报文,就会超时重传连接释放报文段,然后客户端可以在2MSL
时间内收到这个重传的连接释放报文段,接着客户端重传一次确认,重新启动2MSL
计时器,最后客户端和服务端都进入到CLOSE
状态若客户端在
TIME-WAIT
状态不等待一段时间,而是发送完ACK
报文段后立即释放连接,则无法收到服务端重传的连接释放报文段,所以不会再发送一次确认报文段,服务端就无法正常进入到CLOSE
状态防止已失效的连接请求报文段出现在本连接中。
客户端在发送完最后一个
ACK
报文段后,再经过2MSL
,就可以使这个连接所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现旧的连接请求报文段
因为握手的时候并没有数据传输,所以服务端的
SYN
和ACK
报文可以一起发送,但是挥手的时候有数据传输,所以ACK
和FIN
报文不能同时发送,需要分两步,所以会比握手多一步
因为服务端在接收
FIN
,往往不会立即返回FIN
,必须等到服务端所有的报文都发送完毕了,才能发
FIN
。因此先发一个ACK
表示已经收到客户端的FIN
,延迟一段时间才发FIN
。这就造成了四次挥手
如果是三次挥手会造成:
如果将服务端的两次挥手合为一次,等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。所有只能第二次握手先发送ACK确认接收到了客户端的数据,等服务器发送完了数据,再发送FIN包进行第三次挥手
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 接收到它的连接终止请求的确认。
TIME_WAIT 两个存在的理由:
1.可靠的实现tcp全双工连接的终止
2.允许老的重复分节在网络中消逝
CLOSED
:不在连接状态(这是为方便描述假想的状态,实际不存在)
SYN
:同步序列号标志位,tcp三次握?中,第?次会将SYN=1,ACK=0,此时表?这是?个连接请求报?段,对?会将SYN=1,ACK=1,表?同意连接,连接完成之后将SYN=0FIN
:在tcp四次挥?时第?次将FIN=1,表?此报?段的发送?数据已经发送完毕,这是?个释放链接的标志ACK
:当ACK=1时,我们的确认序列号ack才有效,当ACK=0时,确认序号ack?效,TCP规定:所有建?连接的ACK必须全部置为1序号(seq)
:占 32位4 个字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。例如:我们的seq = 201,携带的数据有100,那么最后?个字节的序号就为300,那么下?个报?段就应该从301开始.确认号(ack)
:占 32位4 个字节,期望收到对方下个报文段的第一个数据字节的序号。当标志位ACK值为1时,才能产生有效的确认号ack。并且:ack=seq+1;