• 计算机网络(三) TCP协议详解


    上一篇:计算机网络(二)- TCP/IP协议群介绍

    1、概述

    ​ TCP协议全名是 Transport Control Protocol ,是一个可以提供 可靠的、支持全双工、连接导向的协议,因此在客户端和服务端之间传输数据的时候,是必须先建立连接的。

    image-20211202230056757

    1.1、什么是建立连接

    • 连接本身是个虚拟、抽象的概念。他能让两个通信的程序之间确保彼此都在线
    • 建立连接可以加快相应请求的速度
    • 连接也被称为 会话(Session)
    • 建立连接可以使得通信更加的稳定、安全
    • 但是同样建立连接也会消耗相应的资源

    1.2、单工、半双工、全双工

    • 单工: 任何时刻数据只能单向传输
    • 半双工:允许数据在两个方向上传输, 但是在某一个时刻(同一时刻),只允许数据在一个方向上传输。
    • 全双工: 任何时刻数据都可以双向传输。

    image-20211202231220450

    1.3、可靠性

    • 可靠性是为了保证数据的无损传输
    • 使用无序的数据恢复原有顺序
    • 多播时每个接收方都获得无损的副本。

    2、特点

    • 1、基于连接的(点对点的通信)

      • 传输数据之前是要建立好连接的
    • 2、是双工通信的

      • TCP协议一旦建立连接, 就可以在连上上实现双向的通信
    • 3、基于字节流而非报文--保证了TCP协议的可靠性

      • 将数据按字节大小进行编号,接收端通过ACK来确认收到的数据编号,通过这个机制保证TCP协议的有序性和完整性
    • 4、拥塞控制

      • TCP协议通过 慢启动、拥塞避免、拥塞发生、快速恢复 在不同的过程中的算法来控制拥塞的
    • 5、流量控制能力

      • 通过滑动窗口控制数据的发生速率,滑动窗口的本质是动态缓冲区,接收区根据自己的能力在 TCP 的请求头header中动态调整窗口大小,通过ACK应答包通知到给发送端,发送端根据窗口的大小调控发送速率。

    3、TCP协议详解

    在解释TCP工作流程之前呢, 先解释一下几个名词,这样更有助于我们去了解其过程。

    3.0、TCP首部信息组成以及各个部分的作用

    下图是TCP协议报文段的组成图片

    image-20211202233308672

    image-20211202235746756

    3.0.1、源端口和目的端口
    • 源端口,用来存放 发送TCP报文的进程对应的端口号。占 2个字节(16位)
    • 目的端口,用来存放 接受TCP报文段的进程对应的端口号。 占 2个字节(16位)
    3.0.2、32位序列号 Sequence Number
    • 占用4个字节(一个字节(byte)8个比特位(bit))。
    • TCP的序列号对数据包进行标记,以便达到目的地后重新组装数据包,假设当前序列号为 s ,发送数据长度为 l,则下一次发送数据时的序列号为 s+l
    • 在建立连接时通常由计算机生成随机数作为序列号的初始值。
    3.0.3、32位确认号 Acknowledgement Number
    • 占用 4个字节(一个字节(byte)8个比特位(bit))
    • 是有接收端计算机使用,用于重组分组的报文成最初形式。
    • 如果设置了ACK控制位, 那么这个值表示准备接受的下一个包的序列号
    3.0.4、数据偏移 offset
    • 占用4位,即0.5个字节
    • 这部分实际上是指出TCP报文的首部的长度,即TCP报文段数据起始位置距离TCP报文的起始位置有多远(这里指 TCP拆包后的报文段的起始位置 与 TCP整个报文的起始位置有多远)
    3.0.5、保留字 Reserved
    • 占6位,即 0.75个字节
    • 保留为以后使用,当前是值为零。
    3.0.6、标志位 Tcp Flags
    • 每个标志位占1 位,一共6个标志位 。即 1.5个字节。
    • URG - 紧急指针有效标识
      • 此标志位用来表示TCP包的紧急指针域有效,用来保证TCP连接不会被终端,并且督促中间层设备要尽快处理这些数据。相当于告诉接收端,有高优先级的数据。
    • ACK - 确认序号有效标识
      • 只有当ACK=1的时候,确认号字段才有效。
      • ACK=0的时候,确认号是无效的。
    • PSH - 用来表示接收方应尽快将这个报文段交给应用层
      • 接收到PSH = 1 的TCP报文段, 应尽快的交付接收报文段的应用进程, 而不再等待整个缓存都填满后再交付
    • RST - 重建连接标识
      • RST = 1 时,表明TCP连接出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新简历连接。
    • SYN - 表示同步序号,用来建立连接。
      • SYN = 1 时,表示这是一个连接请求或者连接接受请求。
    • FIN - 发送完成任务标识,用来释放一个连接
      • FIN = 1 表明此报文段的发送端和数据已经发送完成了
    3.0.7、窗口大小 Window Size
    • 占 16位 即两个字节
    • 该字段表明指出了现在允许对方发送的数据量,他告诉对方本端TCP连接缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
    • 窗口大小的值 指的是 从本报文段的首部中的确认号算起, 接方法目前允许对法发送的数据量。
    3.0.8、校验和 TCP Checksum
    • 占用16位,即两个字节。
    • 由发送端填充,接收端对TCP报文段执行CRC算法,以校验TCP报文段在传输过程中是否损坏,如果损坏则丢失。
    • 检验范围包括首部和数据两个部分
    • 这也是TCP可靠性的一个重要保障。
    3.0.9、紧急指针 Urgent Pointer
    • 占用16位,即两个字节
    • 这部分仅在标志位 URG = 1的时候才有意义,他指出本报文段中的数据的字节数。
    • URG = 1 时,发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面任然是普通数据。
    • 因此, 紧急指针指出了数据的末尾在报文段中的位置。

    3.1、TCP协议的三次握手四次挥手

    参考文档 :https://www.cnblogs.com/qdhxhz/p/8470997.html

    先看一下TCP从建立连接到传输数据到断开连接的完整过程

    image-20211204110002227

    3.1.0、三次握手建立连接的过程

    image-20211204100545362

    流程如下:

    • 第一次握手(由客户端发起的)

      • 客户端发送 SYN=1 seq = x 的请求建立连接的报文
        • (SYN 标志位中的同步序列号,用来建立连接的)
        • seq 是 32位序列号 Sequence Number,即在建立连接的时候, 随机生成的初始序列号x。
      • 此时,客户端进程进入了 SYN-SENT(同步已发送状态)状态
      • 此次建立连接的报文是不能携带数据的。
    • 第二次握手(由服务端发起)

      • 服务器接收到请求建立连接的报文后,如果同意连接, 则发出确认报文。
        • 标志位 ACK = 1, 确认号 ack = x+1,这个确认号为 x+1 就是基于上面请求的的seq随机生成的初始序列号+1得出的。
        • 标志位 SYN = 1, 确认号 seq = y , 这个seq 也是服务端自己随机生成的一个初始序列号。
      • 此时 , 服务器进程进入到 SYN-RCVD(同步收到状态)的状态。
      • 这个报文也不能携带数据。
    • 第三次握手(由客户端发起)

      • 客户端收到 服务器发送的的确认报文后, 还要再向服务器发送确认报文。
        • 标志位ACK = 1 , 确认号ack = y+1 ,序列号 seq = x+1
      • 此时 TCP连接建立,客户端和服务端都进入 ESTABLISHED(连接已建立) 状态。
    • 举例解释一下为什么要三次握手

      • 当客户端发送第一个连接的请求报文, 但是由于网络不好,这个请求没有立即到达服务器, 而是在某个节点停留了,知道某个时间才到达服务器,本来这已经是一个失效的报文了, 但是 服务端接收到这个请求报文后,还是会像client 发出确认报文,表示同意建立连接。
      • 假设 不采用三次握手,那么只要服务器发出去确认报文,新的连接就建立了,但是其实这个请求已经超时并失效了,客户端是不会理睬服务器的确认信息的 ,也不会向服务端发送确认的请求。
      • 但是此时的服务器是已经认为连接建立了, 换句话说就是处于ESTABLISHED(连接已建立)的状态,并且一直在等待客户端发来数据。
      • 这样的话, 服务器端就有很多资源浪费了。
      • 而采用三次握手的话, 服务端如果收不到确认报文话,就知道客户端没有服务端建立连接不成功了。
    3.0.2、TCP 数据传输过程

    客户端 与 服务端 建立连接之后,就可以相互传输数据了。

    • 如上图所示,正常传输数据的过程如下:

      image-20211204105540187

      • 主机A 开始发送数据的时候,假设初始的 seq = 1200 ,滑动窗口大小为100,向主机B发送数据传递
      • 主机B在完全接收到数据后, 为了确认收到 ,需要向主机A 发送ACK包,设置的 值为 1200+100+1 .
        • ACK = seq + 传递数据的字节数 + 1
      • 主机A在接收到 主机B 的确认信息后,开始发送下个报文, 此时 seq = 1301 ,同样滑动窗体的大小为100的数据。
      • .......
      • 注: 与 三次握手协议相同, 最后加1 是为了告诉对象要传递 seq信号的。
    • 如上图所示 , 数据丢包的情况过程如下:

      image-20211204110857472

      • seq = 1301 数据包向主机B 传递 100字节的数据,但中间发生了错误,主机B 未收到。
      • 经过一段时间的等待后, 主机A 任然未收到 Seq = 1402 的确认号,此时就会尝试数据重新传递。
      • 为了完成数据包的重传, TCP Socket套接字每次发送数据包时都会启动定时器, 如果在一定时间内没有收到目标主机的传回的ACK包, 那么定时器超时, 数据包会重传。
    3.0.3、四次挥手断开连接

    image-20211204135443345

    • 首先TCP连接断开, 是一个客户端主动关闭, 服务端被动关闭的过程。

    • 关闭连接需要经过4次会话,具体的流程如下:

      • 第一次挥手
        • TCP 连接的客户端发送一个 标志位FIN(结束)=1 的报文,用来请求关闭客户端到服务端的连接。
        • 客户端进程发出释放连接的报文。 释放连接报文的首部,FIN =1,其序列号是 seq = u (序列号等于前面已传送过来的数据的最后一个字节的序号加1)
        • 客户端在发送释放连接的请求后进入FIN-WAIT-1(终止等待1)的状态。
        • TCP协议规定, FIN 报文即时不携带数据也要消耗一个序号。
      • 第二次挥手
        • 服务端收到客户端发送的FIN报文时,客户端会先发送一个 ACK(确认)的报文给客户端。
        • 服务端回复的ACK报文中, ACK = 1 seq = v , ack = u + 1
          • ACK = 1 表示确认报文
          • ack = u + 1 是针对上面第一次挥手服务端的 seq = u 再加上1
          • seq = v 是服务端自己随机生成的序列号。
        • 此时,服务端就进入到CLOSE-WAIT(关闭等待状态)
          • TCP 会通知上层应用进程, 客户端向服务器的方向就释放了。
          • 此时,是处于半关闭状态, 即客户端已经没有数据要发送了, 但是服务器若要发送数据,客户端依然要接收。
          • 这个状态还要持续一段时间, 也就是整个CLOSE-WAIT状态持续的时间。
        • 客户端 在接收到服务器的 断开连接的确认ack报文后,此时
          • 客户端就进入了FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文。
      • 第三次挥手
        • 服务端发送完 ACK 报文后,在确认数据传输完毕后,会发送一条 FIN(结束)的报文到客户端。
        • 由于处于半关闭状态, 服务器可能又发送可一些数据,假定此时的序列号为 seq = w
        • FIN = 1 , ack = u + 1
        • 此时, 服务器就进入 LAST-ACK(最后确认)状态了。
      • 第四次挥手
        • 最后, 客户端收到了服务端的FIN = 1 的报文后,知道客户端已经没有数据需要传输了,由客户端最后再向服务器发送 ACK = 1 的确认报文。并 ack = w(服务端发送的seq)+1 以及 seq = u+2.
        • 此时 客户端进入 TIME-WAIT(时间等待)状态。
        • 注意,此时TCP 连接还没有释放, 必须经过 2 ** MSL(最长报文段寿命) 的时间后, 当前客户端撤销相应的TCB后才进入 CLOSED状态。
        • 服务器端只要接收到客户端发送的确认, 就立即进入CLOSED状态。
        • 综上所述: 服务器端结束TCP连接的时间要比客户端早一些。
    • 思考(一):为什么是 4次挥手?

      • 首先肯定是为了保证数据能个完成全部的传输过程。
      • 当关闭连接时, 当收到对方发送的FIN报文是, 仅仅表示对方没有数据发送给你了。但未必是你发送给对方的数据是否全部发送完毕了,所以你未必可以马上就关闭SOCKET 套接字。所以你可能需要继续传输数据给对方。数据发送完完毕后再发送一个FIN报文给对方。 所以此时服务端的 ACK报文 和 FIN 报文是分开来发送的。
    • 思考(二):数据传输过程中客户端突然挂了怎么办?
      • 正常连接时, 客户端会挂了,如果没有措施处理这种情况的话, 那么就会出现客户端和服务端出现长时间空闲挂起。
      • 解决办法就是在服务器端设置保活计时器,每当服务器收到客户端的消息, 就将计时器复位。计时器的超时时长通常设置为2小时。
      • 如果服务器超过2小时没收到客户端的消息, 他就发送探测报文段,若发送10个探测报文段, 每个相隔75s,还没有相应, 就认为客户端出现故障了, 因而终止该连接。
    • 思考(三): 为什么客户端最后还要等待 2MSL?
      • MSL(Maximun Segment Lifetime) 最长报文寿命

      • 1、保证客户端发送的最后一个ACK报文能够顺利服务器,因此这个报文在数据传输过程中可能会丢失

        • 站在服务器角度来看,服务器已经发送了FIN+ACK 报文请求断开了, 客户端没有给我相应,应该是我发送的请求断开的报文,客户端没有接收到, 于是服务器又会重新发送一次,而客户端就可以再2MSL时间内, 重传这个报文,接着给出ACK报文后, 会重启2MSL计时器。
      • 2、防止已经失效的连接请求报文段出现在在本连接中。

        • 客户端发送完最后一个确认报文后,在这个2MSL时间中, 就可以是本连接吃的时间内所产生的所有报文段都从网络中小时。这样新的连接中不会出现连接的请求报文。
      • 3、那么等待2MSL 就一定没问题了嘛?

        • 不,还有一个超时机制, 超时了,即使没有收到回复也会关闭连接。
    • 思考(4): SYN(洪流)攻击(后面有机会再补充)?
  • 相关阅读:
    C#计算一段程序运行时间的三种方法
    jquery easyui combobox设置默认选中第一项
    ASP.NET Web API教程 分页查询
    ASP.NET Web Api 实现数据的分页
    开源.net 混淆器ConfuserEx介绍
    C#软件license管理(简单软件注册机制)
    MyBatis入门实例-包括实体类与数据库字段对应&CLOB字段处理
    MyBatis在insert插入操作时返回主键ID的配置
    MyBatis框架——mybatis插入数据返回主键(mysql、oracle)
    关于java中split的使用
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/15642114.html
Copyright © 2020-2023  润新知