计算机网络——TCP协议 && 三次握手 && 四次挥手

一、TCP主要特点

  1. TCP是面向连接的运输层协议。应用程序在使用TCP协议之前必须先建立TCP连接。

  2. 每一条TCP连接只能由两个端点(一对一)。

  3. TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。

  4. TCP提供全双工通信

  5. 面向字节流


二、可靠性传输的工作原理

理想传输:
①传输信道不产生差错。
②不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

1. 停止等待协议

分为三种情况:

  1. 无差错情况
    如下,A发送分组M1,发完就暂停发送,等待B确认。B收到M1后就向A发送确认。A收到B的确认后再向B发送下一个分组M2。

这里写图片描述

2.出现差错
  B接收M1时检测出了差错,就丢弃M1(没有通知A收到有差错分组),也可能是M1传输过程中丢失了。此时B不会发送确认消息。这时候可靠传输协议规定:A只要超过一定时间没收到确认消息,就认为刚刚发的分组丢失了,就会重发前面发送过的分组——超时重传。
  为了实现超时重传,要在每次发送完一个分组设置一个超时计时器。如果计时器到期前收到了对方确认,就撤销已设置的超时计时器。此处要注意三点:
  第一:A发完一个分组后,必须暂时保存已发送的分组的副本(为了超时重发),只有收到相应确认后才能清除暂时保留的分组副本。
  第二:分组和确认分组必须编号。这样才能确认哪个发出的分组收到/没收到确认。
  第三:超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。

  1. 确认丢失和确认迟到
      在下图a中,B发送的对M1的确认丢失了。A在设定的超时重传时间内没收到确认,它不知道出了啥岔子,他所能做的就是重传。然后B再次受到M1,B此时要做的是:①丢弃这个重复的M1分组,不向上层交付;②向A发送确认(我已经收到啦,别发了)。
      下图b,传输过程无差错,但B对M1分组的确认迟到了。A会收到重复的确认,对此A收到重复确认直接丢弃。B仍会才收到重复的M1分组(处理方式和上一样)。
    这里写图片描述

三、TCP报文段的首部格式

这里写图片描述

  • 源端口号和目的端口号:各占2个字节,表示数据是从哪个进程来,要到哪个进程去。

  • 序号:占4个字节,序号范围是[ 0,2321 0 , 2 32 − 1 ],序号增加到 2321 2 32 − 1 后,下一个序号又回到0。也就是说,序号采用 mod232 m o d 2 32 运算。TCP面向字节流,传输的字节流中每个字节都按顺序编号。这里的序号指的是本报文段所发送的数据的第一个字节的序号。

  • 确认号:占 4 位,是期望收到对方下一个报文段的第一个数据字节的序号。
    如 确认号 = N,表示:到序号 N-1 为止的所有数据都正确收到。

  • 数据偏移:占4byte,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。它实际上是指出 TCP 报文段的首部长度(首部中有长度不确定的选项字段,因此这个字段是必须的)。“数据偏移”的单位是 32 位字(以4字节为计算单位)。4个byte的二进制数能表示的最大十进制数是 15 ,因此数据偏移的最大值是 60 字节,这也是 TCP 首部的最大长度(选项长度不能超过 40 字节)。

  • 保留:占 6 位,保留为今后使用,但目前应置为 0 。

  • 紧急 URG (URGent)当 URG=1 时,表明紧急指针字段有效。 它告诉系统此报文段中有紧急数据,应尽快传送,而不要按原来的排队顺序来传送。
      当 URG 置 1 时,发送应用进程就告诉对方的 TCP 有紧急数据要传送。于是发送方 TCP 就把紧急数据插到本报文段数据的最前面。这时候要与首部的紧急指针配合使用。

  • 确认 ACK (ACKnowledgment)仅当 ACK=1 时,确认号字段才有效。TCP 规定,在建立连接后所有传送的报文段必须把 ACK 置 1。

  • 推送 PSH (PuSH)提示接收端应用程序立刻从TCP缓冲区把数据读走。此时,发送方 TCP 把 PSH 置 1,并创建一个报文段发送出去。接收方 TCP 收到 PSH=1 的报文段,就尽快地交付接受应用程序,而不是等到整个缓存区满了再向上交付。

  • 复位 RST (ReSeT)当 RST=1 时,表明 TCP 连接中出现严重差错,必须释放连接,再重新建立连接。RST 置 1 还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也称为重建位或重置位。

  • 同步 SYN (SYNchronization):在连接建立是用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文。若对方同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1。

  • 终止 FIN (FINis)用来释放一个连接。 当 FIN=1 时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。

  • 窗口:占 2 字节。窗口值是 [ 0,2161 0 , 2 16 − 1 ] 之间的整数。它指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文首部的确认号算起,接收方目前允许对方发送的数据量(接收方的数据缓存空间是有限的)。总之,窗口值明确指出了现在允许对方发送的数据量(窗口值是经常在动态变化着)。
      例如,我发出一个报文,确认号是 701,窗口值是 1000。这表示,从 701 号算起,我这里还有可以接受 1000 个字节数据(字节序号是 701 ~ 1700)的接受缓存空间。

  • 校验和 :占2字节(16位),由发送端填充,接收端检验报文是否损坏。这个检验不仅包含TCP报文首部,也包含数据部分。

  • 紧急指针:占2字节(16位)。保存的是一个正的偏移量,序号+偏移量就是紧急数据的末尾在这段字节流中的编号。

  • 选项:长度可变,最长为40字节,当没有使用“选项”时,TCP首部长度为20字节。TCP最初只规定了一种选项,即最大报文段长度 MSS,它指的是TCP报文段中数据字段的最大长度


四、三次握手

  TCP 是面向连接的协议。TCP 运输连接的建立和释放是每次面向连接的通信中必不可少的过程。因此,运输连接就有三个阶段,即:连接建立、数据传输和连接释放
  在 TCP 连接建立过程中主要解决以下三个问题:
  1. 要使每一方能确知对方的存在。
  2. 要允许双方协商一些参数(如最大窗口值)。
  3. 能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。


1. 三次握手的具体步骤

这里写图片描述

  1. 主机 A 运行TCP客户端程序,主机 B 运行TCP服务器程序。最初两端的TCP进程都处于 CLOSED(关闭)状态。
  2. B 的 TCP 服务器进程先创建传输控制块 TCB,准备接受客户进程的连接请求。然后服务器进程就处于 LISTEN(收听) 状态,等待客户的请求。如有,即做出响应。

  3. A 的 TCP 客户进程也是首先创建传输控制块 TCB,然后向 B 发出连接请求报文,报文首部中的同步位 SYN=1,同时选择一个初始序号 seq=x。TCP 规定,SYN 报文段(即 SYN=1 的报文段)不能携带数据,但要消耗掉一个序号。此时,TCP 客户进程进入 SYN-SENT(同步已发送)状态。

  4. B 收到连接请求报文段后,如同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置为 1,确认号是 ack=x+1,同时也要为自己选择一个初始序号 seq=y。这个报文段也不能携带数据,也同样消耗一个序号。此时 TCP 服务器进程进入 SYN-RCVD(同步收到) 状态。

  5. TCP 客户进程收到 B 的确认后,还要向 B给出确认确认报文段的 ACK 置 1,确认号 ack=y+1,而自己的序号 seq=x+1。TCP规定:ACK 报文段可以携带数据。但如果不携带则不消耗序号,在这种情况下,下一个数据报文段的序号仍是 seq=x+1。这时 TCP 连接已建立, A进入 ESTABLISHED(已建立连接) 状态。

  6. 当 B 收到 A 的确认后,也进入ESTABLISHED(已建立连接) 状态。


2. 第三次握手的意义

  为什么 A 还要发送一次确认呢?这主要是为了防止已失效的连接请求报文段突然又传送到了 B,因而产生错误。

  正常情况,A 发送连接请求,但因请求连接报文丢失而未收到确认。于是 A 再重传一次连接请求。后来收到确认,建立连接。数据传输完毕后,就释放连接。A 共发了两个连接请求报文段,第一个丢失,第二个到达了 B。没有“已失效的连接请求报文段”
  现假定一种异常情况,即 A 发送的第一个连接请求报文段并没有丢失,而是在某些网络节点长时间滞留了,以至于延误到连接释放后某个时间才到 B。B 收到后以为是 A 又一次发起了连接请求。于是向 A 发出确认报文。假设没有第三次握手,那么只要 B 发出确认,新的连接就建立了。
  由于 A 现在并没有发出连接请求,所以不会搭理 B 的确认,也不会向 B 传输数据。但 B 却认为连接已建立,就一直等待 A 发来数据,这样 B 的资源就白白浪费了。
  采用三次握手后,上述情况就不会发生。A 不会向 B 的确认发出确认。B 收不到确认就知道 A 并没有要求建立连接。


五、四次挥手

1. 四次挥手具体步骤

这里写图片描述

  1. 数据传输结束后,通信双方都可释放连接。现在 A 和 B 都处于 ESTABLISHED 状态。

  2. A 的应用程序先向其 TCP 发出连接释放报文段,并停止发送数据。主动关闭 TCP 连接。A 把连接释放报文段首部的终止控制位 FIN 置 1,其序号 seq=u,他等于前面已传送过的数据的最后一个字节的序号加 1。这时 A 进入 ==FIN-WAIT-1(终止等待1)== 状态。注意,即使 FIN 报文段不携带数据也消耗一个序号。

  3. B收到连接释放报文段后即发出确认,确认号是 ack=u+1,而这个报文段自己的序号是 v,等于 B 前面已传送的数据的最后一个字节的序号加 1.然后 B 就进入 CLOSE-WAIT(关闭等待) 状态。TCP 服务器进程这时应通知高层应用进程,因为从 A 到 B 这个方向的连接就释放了,此时 TCP 处于半关闭(half-close)状态,即 A 已经没有数据要发送了,但 B 若发送数据,A 仍要接受。

  4. A 收到 来自 B 的确认后,就进入 FIN-WAIT-2(终止等待2),等待 B 发出的连接释放报文段。

  5. 若 B 已没有要向 A 发送的数据了,其应用进程就要通知 TCP 释放连接。这时候 B 发出的连接释放报文必须使 FIN=1。现假定 B 的序号为 w(在半关闭状态 B 可能又发送了一些数据)。B 还必须重复发送上次已发送过的确认号 seq=u+1。此时 B 进入 LAST-ACK(最后确认) 状态,等待 A 确认。

  6. A 在收到 B 的连接释放报文段后,必须对此发出确认。在确认报文段中把 ACK 置 1,确认号 ack=w+1,自己的序号是 seq=u+1。然后进入 TIME-WAIT(时间等待) 状态。此时,TCP 连接还没释放掉,必须经过时间等待计时器设置的时间 2MSL 后,A 才进入到 CLOSED 状态。(时间 MSL 叫最长报文寿命


2. 为什么 A 在 TIME-WAIT 状态必须等待 2MSL 的时间?

  第一,为了保证 A 发送的最后一个 ACK 报文段能够到达 B。这个 ACK 报文段有可能丢失,因而使处于 LAST-ACK 状态的 B 收不到对已发送的 FIN+ACK 报文段的确认。B 就会超时重传这个 FIN+ACK 报文段,而 A 就能在 2MSL 时间内收到这个重传的 FIN+ACK报文段。接着 A 重传一次确认,重新启动 2MSL 计时器。最后,A 和 B 都正常进入到 CLOSED 状态。

  第二,防止出现上面提到的“已失效的连接请求报文段”出现在本连接中。A 在发送完最后一个 ACK 报文段,在经过 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。


3. 解决 TIME_WAIT 状态引起的 bind失败的方法

  如果在断开连接过程中,如果是服务器主动断开连接,服务器就会进入 TIME_WAIT 状态,此时想重启服务器就会失败。这种情况是很可怕的,此时就要避免发生这种情况。

#include <sys/socket.h>

int setsockopt(int socket, int level, int option_name,
                           const void *option_value, 
                           size_t option_len);

  使用 setsockopt()函数设置 socket 描述符的选项 SO_REUSEADDR 为 1,表示允许创建端口号相同的多个 socket 描述符。

代码如下:

int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值