TCP三次握手四次挥手
一、三次握手
1.第一次握手
第一次握手前。服务端调用 listen()函数 使其处于 LISTEN 状态(监听状态),然后调用 accept() 函数等待客户端的第一次握手请求。(也就是说服务端一开始就是处于 LISTEN状态 )
客户端通过调用 connect() 函数发起第一次握手请求。客户端将TCP头部数据中的序列号字段
随机初始化值(seq = x,但一般这个 x 会是 0),然后将SYN标志位字段
置1(SYN = 1),表示这是SYN报文。当发送完第一个SYN报文后,客户端处于SYN_SENT状态(同步已发送状态)
2.第二次握手
当服务端收到第一个请求的[SYN]报文时,首先服务端随机初始化值序列号字段
(seq = y,一般也是 y = 0),然后将客户端的seq值+1后填入自己的确认应答号字段
(ack = x+1),再将SYN字段
和ACK字段
都置1,表示这是 [SYN, ACK] 报文。此报文发送后,服务端处于 SYN_RCVD状态(同步收到状态)
3.第三次握手
当客户端收到 [SYN, ACK] 报文后,首先将自己要发送的 [ACK] 报文的TCP头部的确认应答号字段
设置为服务端的seq值+1(ack = y + 1),然后将序列号字段
+1(seq = x +1)。
对于这个
序列号字段
(seq),大家要记住这个序列号其实就是自己发的数据量的序号(不要客户端服务端搞混了,是自己那一方的,仅指自己一方)。比如一开始是seq 设置为1,我发了400个字节的数据给对方,那么下一次我再发的这个seq就要设置为401。但是三次握手不符合这个规则,也就是说大家不必纠结为啥三次握手的seq总感觉不太对劲。
二、四次挥手
1.第一次挥手
客户端打算关闭连接时,会发送一个 [FIN, ACK] 报文,客户端应用层调用close() 函数,传输层会将数据段头部的序列号字段
设为本端已发送的数据量+1(u),将确认应答号字段
设置为已接收服务端的数据量+1(z),并将FIN字段
置为1,表示这是一个 [FIN, ACK] 报文。
这里大家就很清楚了,你是什么性质的报文那么在传输层这一层的时候,就会为报文添加相应的头部数据组成数据段,发到下一层。[FIN, ACK]报文是断开连接报文。[SYN],[SYN, ACK]报文是建立连接报文。[RST],[RST, ACK]报文是异常报文。[PSH]报文是正常的数据报文。[ACK]报文表示确认收到对方报文的报文。
当发送了第一次挥手的报文后,客户端进入 FIN_WAIT_1状态(终止等待状态1)。
2.第二次挥手
当服务端收到客户端发来的[FIN, ACK]报文时,会在进入服务端的物理层时在数据尾插入一个文件结束符EOF字符,到达服务端应用层时,调用的read函数就会先读完前面的数据然后读到最后一个数据EOF时返回。此时服务端将会发送[ACK]报文,下到传输层的时候将头部的序列号字段
设置为本端已发送的数据量+1(v),然后将确认应答号字段
设置为已接收对端的数据量+1(u+1),并将确认字段
置为1,表示这个是[ACK]报文。
服务端发送完[ACK]报文后,就会进入CLOSE_WAIT状态(关闭等待状态)。而客户端在收到这个[ACK]报文后,就会进入FIN_WAIT_2(终止等待2)状态。
3.第三次挥手
进入CLOSE_WAIT状态后,服务端调用close函数,再发送一个[FIN, ACK]包,经过传输层时,和第一次挥手的[FIN, ACK]的行为一致。发完第三次挥手的[FIN, ACK],服务端进入LAST_ACK状态。
4.第四次挥手
客户端收到这个[FIN, ACK]包后,发送[ACK]确认包给服务端,此时客户端就会进入TIME_WAIT状态,经过2MSL时间后直接进入最后CLOSE状态。
而服务端在收到客户端的[ACK]包直接进入CLOSE状态。
三、客/服端的实际调用时机
1.连接时
2.断开连接时
1.某端调用了close()函数,仅表示这一端不再发送数据,但是这一端依然可以接收数据。例如服务端处于CLOSE_WAIT状态时,仍然可以将最后的数据发出去(还没调用close之前),而客户端在FIN_WAIT_1,FIN_WAIT_2状态仍可以接收数据。
2.断开连接哪一端都可以先主动调用close()的,并不是一定是客户端先主动调用的。
加粗样式