TCP协议的三次握手和四次挥手

TCP协议是面向连接的,所以在传输数据前必须先建立连接。

在建立连接的过程中用到了TCP报文的两个序号和三个标志位:

这些序号和标志位在TCP报文的首部定义。下面先来看一下TCP报文段的首部格式:

两个序号和三个标志位的描述如下:

两个序号:seq和ack。

(1)发送序号:seq序号,占32位。TCP连接中传送的字节流中的每一个字节都按顺序编号,TCP首部中的序号字段的意思是本报文段所发送的数据的第一个字节的编号,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

(2)确认序号:ack序号,占32位。期望收到对方下一个报文段的第一个数据字节的编号,也就是说确认号与发送方下一次发送过来的报文段的序号(seq)相等。若确认号是N,则表示已正确收到了对方发送的N-1为止的所有数据。

只有ACK标志位为1时,确认序号字段才有效,ack=对方的seq+1。作用是为了让对方知晓自己理解了对方的意图。

三个标志位:ACK、SYN和FIN。

  (A)ACK:仅当ACK为1的时候确认号(ack)才有效,由于连接建立后就要发送数据,所以连接建立后传输的所有报文段的ACK都要为1。

  (B)SYN:发起一个新连接或同意连接,和ACK一起使用。SYN=1,ACK=0表示这是一个请求连接的报文段;SYN=1,ACK=1表示这是一个同意连接的报文段。

  (C)FIN:释放一个连接。FIN=1时,表示此报文段的发送方的数据已发送完成,要求释放连接。

SYN为synchronize的缩写,ACK为acknowledgment的缩写。

 需要注意的是:

  (1)不要将确认序号ack与标志位中的ACK搞混了。

  (2)确认方ack=发起方seq+1,两端配对。

在建立连接的过程中,客户端和服务器端还会根据连接的阶段不同处于不同的状态,各个状态的名称与含义如下:

CLOSED:客户端和服务器端还没有建立连接的初始状态。 

LISTEN: 服务器端才会有的状态,表示服务器端的某个SOCKET处于监听客户端连接的状态,在这个状态时可以接受客户端的连接请求。 

SYN_SENT: 客户端才会有的状态,当客户端SOCKET执行CONNECT连接时,首先向服务器端发送SYN报文,随即进入SYN_SENT状态,等待服务端的确认。SYN_SENT状态表示客户端已发送SYN报文。当收到服务器端的确认报文后,就会进入到ESTABLISHED状态。 

SYN_RECV: 服务器端才会有的状态,这个状态与SYN_SENT状态遥想呼应,表示接收到客户端的SYN报文。向客户端发送ACK报文,随即进入SYN_RECV状态,这个状态是服务器端的SOCKET在建立TCP连接时的一个中间状态。在这种状态时,当收到客户端的确认报文后,就会进入到ESTABLISHED状态。 

ESTABLISHED:表示连接已经建立的状态。

有了上面的基础,以客户端向服务器发起连接请求为例讲解建立连接需要经历的三次握手的过程:

第一次握手:客户端向服务器发送syn包(syn = 1)表示要请求连接,随机产生客户端发送序号seq = x的数据包到服务器,进入SYN_SEND状态,等待服务器确认(此时ACK默认为0);

第二次握手:服务器收到客户端的syn+seq包,由接收到的syn = 1知道,客户端要求建立连接。就向客户端发送接收收到客户端请求的ack(ack = x+1),随机产生服务器端发送序号seq = y的数据包到客户端,同时自己也发送一个SYN包(syn = 1)和ACK包(ACK=1)表示同意连接,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK+seq+ack包,首先检查ack是否正确,是否是客户端第一次发送的seq的值+1,在这里即判断ack的值是否为x+1,再判断状态码ACK是否为1。若正确,客户端会发送接收到服务器端的确认的ack(ack = y+1),ack 确认序号=(服务器的seq+1),再发送一个ACK包(ACK=1),发送序号seq = x+1的数据包到服务器表示这个客户端发回的应答紧接着第一次对服务器的请求,是同一个客户端,发送完客户端进入ESTABLISHED状态

客户端发送完毕,服务器端收到后确认ack的值是否为服务器端上一次的seq值加1,ACK是否为1,确认无误服务器端进入ESTABLISHED状态此时双方的连接建立成功,完成三次握手。

其中,在三次握手的过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。

理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

下面是“三次握手”的示意图:

断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次挥手”。和“三次握手”类似,也是需要客户端和服务器之间的请求和确认,最终确定断开。

在“四次挥手”的过程中,客户端和服务器端也会有一些状态之间的变化,断开连接的过程中各个状态的名称与含义如下:

ESTABLISHED:已经建立连接,断开连接之前客户端和服务器端的状态。

FIN_WAIT_1:客户端才会有的状态,当客户端的SOCKET在ESTABLISHED状态想主动关闭连接,就向服务器端发送FIN报文,服务器端还没有发回响应时,此时客户端SOCKET就进入到FIN_WAIT_1状态。

这个状态的客户端就不会再向服务器端发送数据了。

CLOSE_WAIT:服务器端才会有的状态,等待关闭的状态。当客户端想要关闭连接时发送FIN报文给服务器,服务器回应一个ACK报文给客户端,此时服务器就进入CLOSE_WAIT状态。然后服务器察看是否还有数据发送给客户端,如果没有就可以关闭这个SOCKET,发送FIN报文给客户端,关闭连接。服务器在CLOSE_WAIT状态下,需要完成的事情是等待发送完数据之后关闭连接。

这个状态的服务器端发送剩余的数据给客户端。

FIN_WAIT_2:客户端才会有的状态,当客户端想要关闭连接向服务器端发送FIN报文,服务器端返回ACK报文之后,客户端就会进入FIN_WAIT_2状态。

这个状态的客户端等待服务器传送剩余的数据给自己,传送完毕再关闭接收数据的连接。 

LAST_ACK: 服务器端才会有的状态,当服务器端向客户端发送完剩下的数据,就会向客户端发送FIN报文,发送完FIN报文之后,进入LAST_ACK状态。最后等待客户端的ACK报文。当服务器端收到ACK报文后,也就可以进入到CLOSED状态了。

这个状态的服务器端就不会向客户端发送数据了。

TIME_WAIT:客户端才会有的状态,表示收到了服务器端的FIN报文。然后向服务器发送ACK报文,进入TIME_WAIT状态。等到2MSL之后就可以进入CLOSED状态。如果FIN_WAIT_1状态下,客户端同时收到了服务器端的带 FIN标志和ACK标志的报文,就可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。当服务器只回应ACK报文时,才进入到FIN_WAIT_2状态。

这个状态的客户端不会发送数据也不会接收数据了。

CLOSED:双方连接完成断开连接的状态,表示连接中止。当客户端向服务器端发送ACK报文经过2MSL后进入CLOSED状态。当服务器端收到客户端的ACK报文之后,进入CLOSED状态。

以客户端向服务器发起断开连接请求为例讲解断开连接的“四次挥手”的过程:

第一次挥手:客户端发送一个FIN请求断开连接,关闭客户端到服务器端的数据传送,此时的客户端发送序号seq = u,随即客户端进入FIN_WAIT_1状态。(与SYN相同,一个FIN占用一个序号)

第二次挥手:服务器端收到FIN后,发送一个ack给客户端,确认序号为从客户端收到的序号+1(ack = u+1),再发送一个ACK = 1标志位,此时的服务器端发送序号seq = v,随即服务器端进入CLOSE_WAIT状态。客户端收到服务器端发送的数据之后,验证收到的序号和标志位,通过验证就进入FIN_WAIT_2状态。

第三次挥手:服务器端发送完数据,向客户端发送一个FIN,用来关闭服务器端到客户端的数据传送,并发送一个ack = u+1的确认序号,再发送一个ACK = 1的标志位。在第二次挥手和第三次挥手的过程中,服务器端到客户端可能又经过了多次数据传送,此时的服务器端发送序号变为seq = w,把服务器端的发送序号seq = w也发送给客户端,然后服务器端进入LAST_ACK状态。发送这个FIN的作用就是服务器告诉客户端我要关闭连接了,你也可以准备关闭连接了。

 第四次挥手:客户端收到FIN后,验证信息,接着发送一个ack给服务器,确认序号为收到的服务器端的序号+1(ack = w+1),再发送一个ACK = 1的标志位,此时的客户端发送序号变为seq = u+1,同时把seq 也发送给服务器端,发送完毕进入TIME_WAIT状态,服务器端收到客户端的确认信息之后进入CLOSED状态。过了2MSL(最大报文段生存时间),客户端也进入CLOSED状态,双方建立的连接结束,完成四次挥手。

客户端向服务器发送ACK的作用就是客户端告诉服务器端我收到服务器的FIN请求了,可以准备关闭连接。不发ACK的话服务器端不知道客户端有没有收到自己发的FIN,客户端没有收到FIN的话不能断开连接的,所以需要第四步的ACK。

下面是“四次挥手”的示意图:

下面有几个问题:

(1)为什么建立连接要“三次握手”,两次不行吗?

假设客户端向服务器端发送了一个连接请求,中间由于网络原因有这个连接请求没有按时到达服务器端,但是也没有丢失。由于客户端发送的请求没有到达服务器端,服务器端也就没有响应,响应超时客户端就会认为这个请求在中途丢失了,就会再次发送一个连接请求。服务器端响应了那些正常到达的连接请求,并建立连接。

然后过了一段时间原先发送的请求到了服务器端,服务器端就会认为这个是客户端的一个新的连接请求,就会发送响应并建立练级,而这个时候客户端已经认为这个请求丢失了,以后对这个请求的响应也就会忽略,导致服务器又建立了连接之后却没有使用从而造成浪费的情况。

而有了第三次握手,服务器端发送确认给客户端(第二次握手),客户端收到确认再发确认给服务器(第三次握手)时,服务器收到确认之后再建立连接,如果没有收到确认不建立连接,就能有效避免上述情况的发生。

(2) 为什么建立连接是“三次握手”,而关闭连接却需要“四次挥手”? 

服务器端在LISTEN状态下的SOCKET收到SYN报文的建立连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一 个报文里来发送。但关闭连接时,当服务器收到客户端的FIN报文通知时,仅仅表示客户端没有数据发送给自己了;但未必服务器端的所有的数据都全部发送给客户端了,这个时候未必可以马上会关闭SOCKET,服务器端可能还需要发送一些数据给客户端之后,再发送FIN报文给客户端来表示服务器现在同意关闭连接了,所以“四次挥手”的ACK报文和FIN报文多数情况下都是分开发送的。

“四次挥手”的话一方发送FIN只表示自己发完了所有要发的数据,但还要允许对方继续把没发完的数据发过来。

举个例子:A和B打电话,通话即将结束后,A说“我没啥要说的了”,B回答“我知道了”,但是B可能还会有要说的话,A不能要求B跟着自己的节奏结束通话,于是B可能又巴拉巴拉说了一通,最后B说“我说完了”,A回答“知道了”,这样通话才算结束。

(3)为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

假如网络是不可靠的,客户端无法保证最后发送的ACK报文一定会被服务器端接收到,服务器端的SOCKET可能会因为超时未收到客户端ACK报文,而重发FIN = 1报文,而这个时候A还处于TIME-WAIT,还可以再接收服务器重发的FIN报文,接受到之后就知道第一次的ACK报文没有发送成功,就再发送ACK确认信息,所以这个TIME_WAIT 状态的作用就是用来重发可能丢失的ACK报文的。

HTTP 1.0和HTTP 1.1建立连接的区别

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。

2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即使不需要获得任何数据,客户端也要保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值