[ 网络原理 ] TCP协议

目录

1. TCP 的报文格式

2. TCP 的相关特性

   2.1 有连接的

   2.2 面向字节流

   2.3 全双工

   2.4 可靠传输( 核心)

3. 可靠传输

   3.1 确认应答

   3.2 超时重传

   3.3 连接管理

   3.4 滑动窗口

   3.5 流量控制

   3.6 拥塞控制

   3.7 延时应答

   3.8 捎带应答

   3.9 面向字节流

   3.10 异常处理

4. TCP 和 UDP 的区别


1. TCP 的报文格式

     

                                 ( 书本上的 TCP 报文段 的格式 如上如 )

   1.1 源端口 、目的端口

      这个与 UDP 中的源端口和目的端口 是一样的 (详见[ 网络原理 ]UDP协议-CSDN博客

   1.2 序号 、确认号 

      见 3.1 确认应答。

   1.3  4位首部长度

      TCP 的报头长度 看似这里只给了 4位首部长度 ,把 TCP 报文长度固定住了,实则不是; TCP 的报头长度是不固定的, 这与 后面的选项有关,当我们选项不选时,报头长度最短就是 20字节;当 我们 选项 选满时,报头最长是 60字节(选项最多是 40字节)。

      那我们只有 4位数 来表示 报头长度 ,4位二进制数最多表示 15 ,所以我们这里的 单位是 4字节

   1.4 保留6位

      UDP 有个问题就是 长度限制在 64kb,TCP就留了个小心机,我搞个保留位,先占个位置,后面如果有需要,在进行拓展,使用。

   1.5 窗口大小

      见 3.4滑动窗口

   1.6 校验和

      与 UDP 相同(详见[ 网络原理 ]UDP协议-CSDN博客

2. TCP 的相关特性

   2.1 有连接的

      TCP 与 UDP 不同,TCP需要通信双方在数据传输之前,必须先建立一个连接,确保数据能够可靠地在网络中传输。

   2.2 面向字节流

      TCP将数据视为一连串的字节流,而不是一系列的消息。这意味着消息的边界不会在TCP头中被保留,应用程序必须自己处理消息的分割和重组。

   2.3 全双工

      TCP允许数据在两个方向上同时传输,即所谓的全双工通信。这意味着在一个TCP连接中,任何一端都可以同时发送和接收数据。

   2.4 可靠传输( 核心)

      TCP通过一系列的机制来确保数据的可靠传输。

3. 可靠传输

   TCP 旨在 可靠传输,为此,TCP构建了一系列机制来确保可靠传输,下面我们来看看都有哪些机制。

   3.1 确认应答

        发送方:将数据发给接收方之后,接收方收到数据就会给发送方发送一个 应答报文(ack),发送方如果 收到了这个应答报文,就知道自己发送成功了。

        但是由于网络中传输数据有可能 会出现“先发后至”的情况,此时,当你连续发多条信息的时候,就不能知道哪条信息(ack)是哪个消息的答复,就可能会出现下述情况:

        所以,在 TCP 报文中就有 一个专门用来标记的信息的发送顺序

    这里 TCP 需要完成两个工作:

        1. 确保应答报文和发出的数据能够对上号

        2. 确保在出现先发后至的情况时,应用程序仍然能按照正确的顺序来理解数据

 那如何理解 32位序号以及32位确认序号呢,见下图:

 注意:

     如何区分一个数据包是 普通的数据包,还是 ack的应答数据呢?

     此时我们就需要继续看看 TCP 的报头结构了:

     在报头结构中,当 ack 位置 为1,表示当前数据包是一个应答报文,并且数据包中的 确认序号字段就会生效;如果为 0,则是一个普通数据包,确认序号不会生效

   3.2 超时重传

     确认应答 是一种比较理想的情况,那如果网络上丢包了,发送方就收不到 ack 了,那连接不就建立不起来吗?

     所以,我们又 增加了超时重传的机制,针对 确认应答进行补充。

     丢包情况 无非两种: 一种是传输的数据丢了, 另一种就是 返回的 ack 丢了

无论发生上述哪种情况,发送方都会进行 “重新传输”。

假设丢包的概率 为 10%,那重传之后,两次丢包概率 就变成了 1%

所以说,重传操作大大提升了 数据能够被传过去的概率 

注意:

    1. 那发送方何时 进行重传呢?

        其实它是有一个等待时间的,当发送方 发出数据一段时间之后,如果在这段时间内,收到 ack ,就视为 数据到达,如果没有收到 ack ,就会视为丢包(可能是数据,也可能是ack),此时就会触发重传机制。

    2. 那 如果 接收方 突然断开网络,那发送方 会一直 重传吗?

        当然不会,当重传第一次 还是没有收到,此时第二次等待的时间会比第一次更长,时间拉长也不是无限拉长,当达到一定程度时,就会认为无论 重发 多少次,都不可能到达,此时就会放弃 tcp 连接了。

    3. 当 ack 丢失的时候,发送方就会重传,那接收方不就收到相同的数据了吗,那怎么处理呢?

        其实 TCP 已经很贴心的帮我们将这个问题给解决了, TCP 会有一个 接收缓存区 ,也就是一个内存空间,会保存当前已经收到的数据,以及数据的序号,当发送数据重复时, 接收方就会直接把后来的数据给丢弃,确保应用程序 在读取数据的时候,不会读到同一个数据多次。

   3.3 连接管理

     (1)建立连接 --- 三次握手

           这里的三次握手,只是打招呼,并没有实际意义,只是为了唤起对方的注意

     这里的三次握手就好比于:

           

     在 TCP 中,“ 三次握手 ”发送的数据包 并不是 真正要发送的数据,而是一个 特殊的 TCP 数据报,它在 TCP 的报头中有 显示(syn 位为1 ,表示 是同步报文段;为0,则不是):

     所以上述 握手就可以 看成这样:

综上, 三次握手的 作用 有这些:

      (1) 确保当前网络 是否通畅

      (2) 让 发送方和接收方 都能确认自己的发送能力和接收能力 均正常

      (3) 让通信双方在握手的过程中,协商一些重要的参数(比如 tcp 的序号从哪开始)

     (2) 断开连接 --- 四次挥手

          断开连接 本质上跟 建立连接差不多。

    

          这里 主机 A 跟 主机B 说 它要断开连接 ,其实是 发送了 一个 结束报文段(FIN):

那 这里能不能跟 上述建立连接一样,将 FIN 和 ACK 一起发送过去 呢?

          答案显然是不行的,首先 在建立连接的时候,ACK和SYN 都是内核触发的,属于同一个时机,所以可以合并在一起;而这里 断开连接,FIN 是 应用程序执行 完 close 之后触发的,实际并不相同,所以不能合并成 三次挥手

 注意:

  发送方A 是否收到 接收方B 发送的 FIN 就会断开连接呢?

        答案显然 不会。你看,如果 发送方A 接收到 接收方B 发送的 FIN 就直接断开连接了,ACK 恰好丢包了,那 接收方B 就会接收不到 ACK ,就会在一段时间之后触发 超时重传,但是 发送方A 已经断开连接了,那 接收方B 就一直 触发超时重传,但是 B 永远也就收不到 ACK 了。

        所以,当 A 收到 FIN 之后,会先处于 TIME_WAIT 状态,并且等待 2MSL( 网络上两节点通信消耗的最大时间为 MSL)之后,如果还没有收到 B 超时重传的 FIN ,此时 A 才会断开连接。

     如下图:

       

          前面三个机制,都是在确保 tcp的可靠性,但是,提升可靠性的同时,效率却降低了,为让 可靠传输对性能的影响更小一些,就引入了 滑动窗口 。

   3.4 滑动窗口

          刚才我们讨论了确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送 下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。

          既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多 个段的等待时间重叠在一起了)。

1.  窗口大小 指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是 4000 个字节(四个段)。
2. 发送前四个段的时候,不需要等待任何 ACK ,直接发送;
3. 收到第一个 ACK 后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
4. 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有 应答;只有确认应答过的数据,才能从缓冲区删掉;
5. 窗口越大,则网络的吞吐率就越高;

     那么如果出现了丢包,如何进行重传?

 情景一: 数据包没丢,应答报文 ACK 丢了

这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认;

情景二: 数据包丢了

 

(1)当某一段报文段丢失之后,发送端会一直收到 1001 这样的 ACK ,就像是在提醒发送端 " 我想 要的是 1001" 一样;
(2)如果发送端主机连续三次收到了同样一个 "1001" 这样的应答,就会将对应的数据 1001 - 2000 重新发送;
(3) 这个时候接收端收到了 1001 之后,再次返回的 ACK 就是 7001 了(因为 2001 - 7000 )接收端 其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区 中;
   3.5 流量控制
      接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发 送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做 流量控制。
(1) 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 " 窗口大小 " 字段,通过 ACK 端通知 发送端;
(2)窗口大小字段越大,说明网络的吞吐量越高;
(3)接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端; 发送端接受到这个窗口之后,就会减慢自己的发送速度;
(4) 如果接收端缓冲区满了,就会将窗口置为0 ;这时发送方不再发送数据,但是需要定期发送 一个 窗口探测数据段 ,使接收端把窗口大小告诉发送端。

 

   3.6 拥塞控制
      虽然TCP 有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大 量的数据,仍然可能引发问题。
      因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发 送大量的数据,是很有可能引起雪上加霜的。
      TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传 输数据;
     (1) 当TCP 开始启动的时候,慢启动阈值等于窗口最大值;
     (2) 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1
     (3) 少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞;
     (4) 当TCP 通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;拥塞控  制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的 折中方案。
       TCP 拥塞控制这样的过程,就好像 热恋的感觉
   3.7 延时应答
      如果接收数据的主机立刻返回ACK 应答,这时候返回的窗口可能比较小。
(1)假设接收端缓冲区为 1M 。一次收到了 500K 的数据;如果立刻应答,返回的窗口就是 500K
(2)但实际上可能处理端处理的速度很快, 10ms 之内就把 500K 数据从缓冲区消费掉了;
在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过
来;
(3)如果接收端稍微等一会再应答,比如等待 200ms 再应答,那么这个时候返回的窗口大小就是 1M;
      一定要记得, 窗口越大,网络吞吐量就越大,传输效率就越高 。我们的目标是在保证网络不拥塞的情况 下尽量提高传输效率;
      那么所有的包都可以延迟应答么?肯定也不是
数量限制:每隔 N 个包就应答一次;
时间限制:超过最大延迟时间就应答一次;
   3.8 捎带应答
      在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 " 一发一收 " 的。意味着客 户端给服务器说了 "How are you" ,服务器也会给客户端回一个 "Fine, thank you"
      那么这个时候ACK 就可以搭顺风车,和服务器回应的 "Fine thank you" 一起回给客户端;这里的捎带应答在 三次握手中 有所体现。
   3.9 面向字节流

        由于 TCP 在数据传输的时候是面向字节流的,接收方在接受的时候会先创建一个接收缓存区,将 多个 应用层的数据包的数据,以字节的形式 仅仅 挨在一起,那 接收方 的应用程序在 读取数据的时候,由于 所有的数据包的数据都紧挨在一起,接收方就无法 判断 从哪个字节到哪个字节是一个完整的 数据包了。这就是 粘包问题。

       粘包问题

       粘包问题就是,接收方无法判断 每个数据包 的边界,那么要解决粘包问题,就给它规定好边界即可。

       1. 引入 分隔符

       2. 引入长度

   3.10 异常处理
     (1) 进程崩溃
           进程终止会释放文件描述符,相当于调用了socket.close(),此时就触发FIN,对方收到之后们就会返回FIN 和 ACK ,这边在进行 ACK ,这 和正常关闭没有什么区别。

     (2)主机关机

           当我们的主机关机的时候,它会先触发 强制终止进程,此时就相当于进程崩溃了,情况于进程崩溃相同。
           当对端的 ACK 和 FIN 在我们主机关闭之前到了,我们还是正常的 四次挥手 结束的;如果在主机关闭后到达,那么 对端就无法收到 ACK ,它就会触发超时重传,重传几次之后还是没有响应,它就会放弃连接。
     (3) 主机掉电/ 网线断开
           这是一瞬间的事情,主机还来不及杀进程,来不及发送 FIN,主机就停机了。
           站在对端的角度:
           1.  如果对端发信息,它发现 一直接收不到 ACK,此时就会触发 TCP 连接重置功能,发起 “ 复位报文段 ”(RST) ,如果发送 RST 之后还是没有效果,它就会释放连接。
           2.  如果对端在接收消息,接收到一半,等待半天都没消息,此时就无法区分是对端没发信息,还是对端挂了。 所以 TCP 中提供了一个 心跳包 的机制:接收方会 周期性的给发送方发起一个特殊的,不带 业务数据的 数据包,期望对方的应答,如果对方多次没有应答,就会视为对方挂了,此时就可以单方面释放连接了。

4. TCP 和 UDP 的区别

    TCP 用于可靠传输的情况,应用于文件传输,重要状态更新等场景;
    UDP 用于对高速传输和实时性要求较高的通信领域,例如,早期的 QQ ,视频传输等。另外
UDP 可以用于广播;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值