Tcp三次握手四次挥手

作为大篇幅涉及TCP的第一篇,所以肯定不止会只讲三次握手与四次挥手,本篇主要会从TCP是什么,三次握手与四次挥手谈起。

What is TCP?

传输控制协议 TCP (Transmission Control Protocol), 在运输层提供面向连接的服务。在其上有应用层,运输层为其提供通信服务,运输层属于面向通信部分的最高层,同时也是用户功能的最底层。

TCP的几个特点

  1. TCP是面向连接的运输层协议。
  2. 每条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。
  3. TCP提供可靠交付服务。
  4. TCP提供全双工通信。
  5. 面向字节流。

这几个特点只是简单提及,在以候几篇TCP中会详细谈到。。

TCP报文段的首部格式

TCP虽然面向字节流,但TCP传送的数据单元却是报文段。话不多说,看图,TCP的格式是要烂熟于心的。

TCP_format

可见,TCP报文段首部的前20字节是固定的。后面有4n(n为整数)字节是根据需要而增加的选项。所以可说TCP的首部最小20字节。

首部固定各部分意义如下:

  • 源端口和目的端口字段——各占 2 字节。端口是运输层与应用层的服务接口。运输层的复用和分用功能都要通过端口才能实现。
  • 序号字段——占 4 字节。TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。
  • 确认号字段——占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。
  • 数据偏移(即首部长度)——占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。“数据偏移”的单位是 32 位字(以 4 字节为计算单位)。
  • 保留字段——占 6 位,保留为今后使用,但目前应置为 0。
  • 紧急 URG —— 当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。
  • 确认 ACK —— 只有当 ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号无效。
  • 推送 PSH (PuSH) —— 接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。
  • 复位 RST (ReSeT) —— 当 RST = 1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
  • 同步 SYN —— 同步 SYN = 1 表示这是一个连接请求或连接接受报文。
  • 终止 FIN (FINish) —— 用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。
  • 窗口字段 —— 占 2 字节,用来让对方设置发送窗口的依据,单位为字节。
  • 检验和 —— 占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
  • 紧急指针字段 —— 占 16 位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。

选项字段 —— 长度可变。TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP:“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。 MSS (Maximum Segment Size)是 TCP 报文段中的数据字段的最大长度。数据字段加上 TCP 首部才等于整个的 TCP 报文段。所以,MSS=TCP 报文段长度- TCP 首部长度

填充字段 —— 这是为了使整个首部长度是 4 字节的整数倍。

为何要有MSS?

  • MSS 与接收窗口值没有关系。
  • 若选择较小的 MSS 长度,网络的利用率就降低。
  • 当 TCP 报文段只含有 1 字节的数据时,在IP 层传输的数据报的开销至少有40 字节(包括TCP 报文段的首部和IP 数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。
  • 若 TCP 报文段非常长,那么在IP 层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的TCP 报文段。当传输出错时还要进行重传。这些也都会使开销增大。

所以,MSS要尽可能大,使报文在IP层时不再需要分片。又由于IP数据报所经历路径是动态变化的,所以,一条路径上不需要分片的MSS,如若改走另一条路径可能需要进行分片,故最佳MSS是很难确定的。

三次握手

上面也一直在强调,TCP是一个面向连接的协议。既然面向连接,那就离不开三个阶段:连接建立、数据传送、连接释放。

先说连接建立,TCP把建立连接的过程叫做握手,很形象,了解了三次握手的主要过程,体会会更深刻。下图则是对三次握手的一次概括:

TCP_handshake

  1. 第一次握手:客户端A向服务器B发送连接请求报文段,将首部中同部位SYN置1,同时选择了一个初始序号seq为x;然后客户进程进入SYN-SENT(同步收到)状态。ps:在TCP规定中,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号
  2. 第二次握手:服务器B收到请求报文段后,如同意建立连接,则将SYNACK都置1,设置确认号为ack=x+1;同时也要为自己选择一个初始序号seq为y。同样这个报文段也不能携带数据,同样也要消耗掉一个序号。此时的TCP服务进程进入SYN-RCVD(同步收到)状态。
  3. 第三次握手:客户端A收到B的确认(SYN+ACK报文段)后,当然也需要向B给出确认。将报文ACk置1,确认号ack=y+1,自己序号为seq=x+1。TCP规定,ACK报文段可以携带数据,不携带数据不消耗序号,所以下一个报文段的序号仍为seq=x+1。这是连接建立,A进入ESTABLISHED(已建立连接)状态。B收到确认后,也进入此状态。

思考:为什么是三次握手?

​ 如果没有最后的确认,当A发送请求,B一确认就建立连接的话,如果A连发两次请求,第一次请求在网络中滞留,延误后才到达的B,B收到后误认为在收到A实际的第二次请求后,A又重新发送请求,于是确认,但A并没有理睬B的确认,也没有向B发送数据,此时连接已建立,B一直苦苦等待A,B的资源就会被浪费。所以要有最后一次的握手确认,以避免此类服务器等待而发生资源浪费问题的发生。

四次挥手

TCP_bye

  1. 第一次挥手:A将首部终止控制位FIN置1,其序号seq为u,它等于前面已传送过的数据的最后一个字节序号加1.此时A进入FIN-WAIT-1(终止等待1)状态,等待B确认。TCP规定,FIN报文段即使不携带数据,其也得消耗一个序号。
  2. 第二次挥手:B收到报文段后随即确认,确认号为ack=u+1,这时报文段序号为v,等于B之前传送过数据的最后一个字节的序号加1。然后B进入CLOSE-WAIT(关闭等待)状态。此时,TCP服务器对于高层的一声通知,A到B的连接也就随即释放了,可以说此时连接处于半关闭状态,即A已经没有数据要发送给B了,如果B坚持要发,A还是要接收的(B到A的连接还未关闭)。A收到B的确认进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放报文段。
  3. 第三次挥手:若B也没有要向A发送的数据了,那么B发出的报文段须置FIN为1,假定在半关闭时B又发送了些数据,序号变为了w。B还必须重复发送已发送过的确认号ack=u+1。此时B就进入了LAST-ACK(最后确认)状态。
  4. 第四次挥手:A收到B的释放报文段,必须确认。在确认报文中把ACK置1,确认号为ack=w+1,序号则为seq=u+1,然后A进入TIME-WAIT(时间等待)状态。注意:此时A必须经过时间等待计时器设置的2MSL后,才可进入到CLOSED状态。其中MSL为最长报文段寿命一般为2分钟。

为什么要进入TIME-WAIT状态等待

  1. 为了保证 A 发送的最后一个 ACK 报文段能够到达 B。如A发送的这个ACK报文段丢失,B收不到确认,B会重传,如果A不等待,A就没有了机会去接收到B重传的FIN+ACK报文段,因此B就无法进入CLOSED状态。
  2. 防止 “已失效的连接请求报文段”出现在本连接中。A 在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段,都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

所以,可以确定,B结束连接的时间要早于A