【网络原理 3】TCP协议的相关特性(三次握手,四次挥手)(万字详解)看完必懂 !

🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇

TCP相关重要特性

🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇

今日推荐歌曲: death bed (coffee for your head ) -- Powfu / beabadoobee 🎵🎵


系列文章目录

【 网络原理 1】 网络编程原理基础知识-CSDN博客

【网络原理 2】UDP 协议的报文结构和注意事项-CSDN博客


目录

前言

TCP协议

[1. 可靠性](#1. 可靠性)

确认应答

[TCP 中的序列号](#TCP 中的序列号)

[2. 超时重传](#2. 超时重传)

[3. 连接管理 ⭐⭐⭐](#3. 连接管理 ⭐⭐⭐)

建立连接:三次握手⭐

断开连接:四次挥手

TCP状态转换的汇总:

[4. 滑动窗口](#4. 滑动窗口)

[5. 流量控制](#5. 流量控制)

[6. 拥塞控制](#6. 拥塞控制)

[7. 延迟应答](#7. 延迟应答)

[8. 捎带应答](#8. 捎带应答)

[9. 面向字节流](#9. 面向字节流)

粘包问题

[10. 异常情况](#10. 异常情况)

[1. 进程崩溃](#1. 进程崩溃)

[2. 正常关机](#2. 正常关机)

[3. 突然断电](#3. 突然断电)

[​编辑4. 网线断开](#编辑4. 网线断开)

TCP/UDP对⽐

总结


前言

这篇文章会讲述 TCP 协议的一些相关特性。


TCP协议

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。它负责在网络上可靠地传输数据,确保数据的完整性、顺序性和可靠性。TCP协议通过面向连接、可靠传输、流量控制、拥塞控制等机制,保证了数据的可靠性、完整性和顺序性,是互联网上应用最广泛的传输层协议之一。

TCP协议段格式:

• 源/⽬的端⼝号:表⽰数据是从哪个进程来,到哪个进程去;

**• 32位序号/32位确认号:**每⼀个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下⼀次你从哪⾥开始 发

• 4位TCP报头⻓度:表⽰该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最⼤⻓度是15* 4 =60

• 6位标志位:

◦ URG:紧急指针是否有效 ◦ ACK:确认号是否有效

◦ PSH:提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛

◦ RST:对⽅要求重新建⽴连接;我们把携带RST标识的称为复位报⽂段

◦ SYN:请求建⽴连接;我们把携带SYN标识的称为同步报⽂段

◦ FIN:通知对⽅,本端要关闭了,我们称携带FIN标识的为结束报⽂段

• 16位窗⼝⼤⼩:发送数据时无需等待确认应答而可以继续发送数据的最大值 .

• 16位校验和:发送端填充,CRC校验.接收端校验不通过,则认为数据有问题.此处的检验和不光包含 TCP⾸部,也包含TCP数据部分.

• 16位紧急指针:标识哪部分数据是紧急数据;

**• 40字节头部选项:**TCP 中也有很多参数要协商的,往往以"选项" 部分来体现的。


1. 可靠性

1.1 确认应答

网络通信过程是复杂的.无法确保发送方发出去的数据,100%能够到达接收方.
此处可靠性,只能"退而求其次",只要尽可能的去进行发送了,发送方能够知道对方是否收到,就认为是可靠传输了.

TCP安身立命的本钱.初心就是解决 " 可靠传输 " 问题.

而 确认应答(ACK) 是TCP 可靠性的核心机制

ACK(Acknowledgement,确认)是TCP协议中的一种控制消息,用于确认已经成功接收到数据。在TCP的数据传输过程中,接收方会向发送方发送ACK消息,以告知发送方已经成功接收到数据。确认号(ACK number)是ACK消息中的一个字段,表示接收方期望下一个收到的数据的序列号。

发送方和接收放就是以上图(发送一个 ack )的方式传输数据确认数据的确认送达

1.2 TCP 中的序列号

除 ack 外,TCP将每个字节的数据都进⾏了编号.即为序列号.

每⼀个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下⼀次你从哪⾥开始 发

那么序列号是怎么构建的呢?

应答报文中的确认序号,是按照发送过去的最后一个字节的序号再加1来进行设定的~


2. 超时重传

超时重传是确认应答的补充

如果一切顺利,通过应答报文就可以告诉发送方,当前数据是不是成功收到.

但是,网络上可能存在"丢包"情况.如果数据包丢了,没有到达对方,对方自然也没有ack报文了.这个情况下,就需委超时重传了.

TCP可靠性就是在对抗丢包,期望在丢包客观存在的背景下,也能够尽可能的把包给传过去。

如果主机A在⼀个特定时间间隔内没有收到B发来的确认应答,就会进⾏重发;这就是超时重传

丢包分两种情况:

  1. 主机A发送数据给B之后,可能因为⽹络拥堵等原因,数据⽆法到达主机B;

那么服务器就会隔一段时间(阈值)再次发送数据1(1~1000)

  1. 主机A未收到B发来的确认应答,也可能是因为ACK丢失了;这里服务器会隔一段时间就会重传 数据 1(1~1000)。

因此主机B会收到很多重复数据.那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉.

对于这个问题:

TCP socket在内核中存在接收缓冲区(一块内存空间)发送方发来的数据,是要先放到接收缓冲区中的. 然后应用程序调用read / scanner.next才能读到数据.这里的读操作其实是读接收缓冲区.

当数据到达接收缓冲区的时候,接收方首先会先判定一下看当前缓冲区中是否已经有这个数据了(或者这个数据曾经在接收缓冲区中存在过),如果已经存在或者存在过,就直接把重复发来的数据就丢弃了,就能确保应用程序,调用read / scanner.next的时候,不会出现重复数据了.

接收缓冲区,除了能够帮助我们进行去重之外,还能够进行排序.
对收到的数据进行排序.按照序号来排序.确保应用程序读到的数据和发送的数据顺序是---致的!!
就能确保应用程序,调用read / scanner.next的时候,不会出现重复数据了.

这里可以把缓冲区抽象成一个带有优先队的阻塞队列(而且还有去重效果)

接收方如何判定这个数据是否是 " 重复数据 " 核心判定依据就是 数据的序号

  1. 数据还在接受缓冲区里,还没被read走.此时,就拿着新收到的数据的序号,和缓冲区中的所有数据的序号对一下,,看看有没有一样的.有一样的就是重复了.就可以把新收到的数据丢弃了.
  2. 数据在接受缓冲区中,已经被应用程序给read走了,此时新来的数据序号直接无法再接受缓冲区查到

注意 !! 应用程序读取数据的时候,是按照序号的先后顺序,连续读取的!!

先读1-10001001-20002001-3000

一定是先读序号小的数据后读序号大的数据的(可以把接收缓冲区这个队列想象成带有优先级的阻塞队列)

此时 socket api中就可以记录上次读的最后一个字节的序号是多少

比如上次读的最后一个字节的序号是3000

新收到一个数据包的序号是1001 ,

这个1001一定是之前已经读过的了.这个时候同样可以把这个新的数据包判定为"重复的包"直接丢弃了.

超时是会重传,重传也不是无限的重传

重传过程也是有一定的策略

  1. 重传次数是有上限的.重传到一定程度,还没有ack,就尝试重置连接,如果重置也失败,就直接放弃连接.
  2. 重传的超时时间阈值也不是固定不变的,随着重传次数的增加,而增大(重传频率越来越低)

经历了重传之后还丢包,大概率是网络出现严重问顾了,再怎么重传也是白费劲.重传还是要重传

那么,如果超时的时间如何确定?

• 最理想的情况下,找到⼀个最⼩的时间,保证"确认应答⼀定能在这个时间内返回".

• 但是这个时间的⻓短,随着⽹络环境的不同,是有差异的.

• 如果超时时间设的太⻓,会影响整体的重传效率;

• 如果超时时间设的太短,有可能会频繁发送重复的包; TCP为了保证⽆论在任何环境下都能⽐较⾼性能的通信,因此会动态计算这个最⼤超时时间.

• Linux中(BSDUnix和Windows也是如此),超时以500ms为⼀个单位进⾏控制,每次判定超时重发的 超时时间都是500ms的整数倍.

• 如果重发⼀次之后,仍然得不到应答,等待2*500ms后再进⾏重传.

• 如果仍然得不到应答,等待4*500ms进⾏重传.依次类推,以指数形式递增.

• 累计到⼀定的重传次数,TCP认为⽹络或者对端主机出现异常,强制关闭连接.


3. 连接管理 ⭐⭐⭐

在正常情况下,TCP要经过三次握⼿建⽴连接,四次挥⼿断开连接。

⚽ 建⽴连接的意义:

1. 投⽯问路,确认当前通信路径是否畅通.

2. 协商参数,通信双⽅共同确认⼀些通信中的必备参数数值.

3.1 建立连接:三次握手⭐

内核是怎样完成上述的 "建立连接" 过程的呢 ??
称为"三次握手"此处谈到的连接"虚拟的,抽象的"连接.承的是让通信双方都能保存对方的相关信息

所谓的建立连接过程,本质上就是通信双方各自给对方发起一个syn,各自给对方回应一个ack.(这里客户端的信息告知服务器这个操作确实在第一次握手的时候就完成了,但是最终确立出这个连接要建立,确立出后续要进行通信,还是得所有的流程都走完)

上述的过程中,画了四次交互,实际上中间两次可以合并.

syn就是第五位为1

ack就是第二位为1

完全可以有一个数据包,第五位和第二位都是1,

此时这个数据包就同时起到了两个作用,既能够应答上个请求,也能发起syn

网络传输过程中要涉及到多次的封装和分用.

两个包就封装分用两次.合并成一个包,就可以减少━次封装分用的过程.整体的效率就提升了,成本就降低了.

三次握手的意义:

  1. 确认链路是否畅通
  2. 确认接收方和发送方的发送接收能力是否正常
  3. 协商一些必要的参数,一些必要的参数要保持一致。

TCP 中也有很多参数要协商的,往往以"选项" 部分来体现的。

最少 0 字节,最多 40 字节(TCP 报头总长度最多 60,去掉前面固定的 20 ,还剩下 40)

其中有一个信息是挺关键的,TCP通信的序号,起始值,TCP一次通信过程中,序号不是从О或者从1开始计算的.,而是先选择一个比较大的数字,以这个数字开头来继续计算,即使是同一个客户端和服务器,每次连接,开始的序号都不同,避免出现一个情况, "前朝的剑,斩本朝的官"

第一次连接的过程中,传输的有一个数据包,在路上堵车了.迟迟没有到达对端.
等到终于到了对端的时候,已经改朝换代了.之前的连接早都没了,现在是新的连接了!!
此时,这份数据,就应该被丢弃!!
数据报时按照 ip+端口进行识别的.
第一个连接,是用客户端A来连的.第二个连接用客户端B来连的.(恰好是同一个端口的话,客户端概率是比较低。服务器概率很大)
此时数据到达这一边,早已物是人非.这个时候的话,再来进行处理这个数据就不合适了.
此时,丢弃这个数据包是一个上策.
如何识别出,当前的数据是"前朝"的数据包呢??就可以通过序号来区分!!
理论上也是存在,但是概率很低
(序号是不同,也不是随机的,背后还有一系列分配策略)
正常的数据包的序号都是从开始序号往后依次排的.就算偶尔丢包,偶尔数据不连续,差异不会很大.
前朝的包,序号就会和本朝的包序号差异非常大,很容易一眼识别出来.

3.2 断开连接:四次挥手

连接本质上就是让通信双方保存对方的信息!!怎么保存??每个客户端/服务器,都要保存很多的对端的信息,---旦多了,就需要使用"数据结构",断开连接的本质目的,就是为了把对端的信息\从数据结构中给删除掉/释放掉

TCP协议的四次挥手是在TCP连接关闭时,用于确保双方都知道连接已经关闭的过程。以下是四次挥手的描述:

  1. 第一次挥手:

    • 主动关闭方发送一个FIN(Finish)标志位设置为1的报文段给被动关闭方,表示主动关闭方已经完成了数据的发送任务,并且不再发送数据,但是仍然可以接收数据。
  2. 第二次挥手:

    • 被动关闭方收到主动关闭方的FIN报文段后,会发送一个ACK(Acknowledgment)确认报文段给主动关闭方,确认已收到关闭请求,并且会进入CLOSE_WAIT状态,表示自己已经没有数据要发送了。
  3. 第三次挥手:

    • 被动关闭方发送一个FIN标志位设置为1的报文段给主动关闭方,表示被动关闭方也完成了数据的发送任务,并且不再发送数据,但是仍然可以接收数据。
  4. 第四次挥手:

    • 主动关闭方收到被动关闭方的FIN报文段后,发送一个ACK确认报文段给被动关闭方,确认已收到关闭请求,并进入TIME_WAIT状态,等待2MSL后进入CLOSED状态。被动关闭方收到这个ACK后,进入CLOSED状态。

通过这四次挥手过程,双方可以确保数据的完整传输,最终关闭连接。这样的挥手过程保证了网络通信的可靠性和数据的完整性。.

那么第二次和第三次挥手可以合并吗?

实际上,第二次挥手和第三次挥手是可以合并的,并不一定要分开发送。在实践中,有些TCP实现确实会将第二次挥手和第三次挥手合并在一起,以减少连接关闭所需的时间。

然而,在一些情况下,分开发送第二次和第三次挥手可能更为安全可靠,这取决于TCP栈的实现和网络环境。下面是一些可能导致分开发送的情况:

  1. 应用层数据处理延迟: 在某些情况下,应用程序可能需要更多时间来处理接收到的数据,因此在发送第二次挥手之前可能需要等待一段时间。这种情况下,分开发送第二次和第三次挥手可以让被动关闭方更好地控制数据的处理。

  2. TCP实现的特定要求: 一些TCP栈的实现可能需要在发送第二次挥手之后等待一段时间,以确保对方已经接收到了FIN报文。在这种情况下,分开发送第二次和第三次挥手可以更好地控制连接关闭的时机。

  3. 网络延迟和拥塞: 在网络环境不稳定或存在延迟和拥塞的情况下,分开发送第二次和第三次挥手可以减少由于丢包或延迟而导致的不确定性。这样可以更好地确保双方都正确地接收到了关闭请求。

虽然第二次挥手和第三次挥手可以合并,但在某些情况下分开发送可能更为可靠。因此,具体是否合并发送取决于TCP栈的实现和网络环境的情况。

3.3 TCP状态转换的汇总:

• 较粗的虚线表⽰服务端的状态变化情况;

• 较粗的实线表⽰客⼾端的状态变化情况;

• CLOSED是⼀个假想的起始点,不是真实状态; 这 ⾥我们简单了解即可.

TIME_WAIT 想⼀想,为什么是TIME_WAIT的时间是2MSL?

• MSL是TCP报⽂的最⼤⽣存时间,因此TIME_WAIT持续存在2MSL的话

• 就能保证在两个传输⽅向上的尚未被接收或迟到的报⽂段都已经消失(否则服务器⽴刻重启,可能会 收到来⾃上⼀个进程的迟到的数据,但是这种数据很可能是错误的);

• 同时也是在理论上保证最后⼀个报⽂可靠到达(假设最后⼀个ACK丢失,那么服务器会再重发⼀个 FIN. 这时虽然客⼾端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK);

CLOSE_WAIT ⼀般⽽⾔,对于服务器上出现⼤量的CLOSE_WAIT状态,原因就是服务器没有正确的关闭socket,导致 四次挥⼿没有正确完成.这是⼀个BUG.只需要加上对应的close即可解决问题.

listen 状态:等待接收 连接

established 状态 :确立已久的

close_wait 状态:谁被动断开连接进入 close_time 状态 表示接下来代码中需要调用 close 来发起 fin 然后转成 LAST_ACK 状态

Time_wait 状态:

Last_ACK:等待最后一个ACK。


4. 滑动窗口

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

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

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

那么如果出现了丢包,如何进⾏重传?这⾥分两种情况讨论.

情况⼀:数据包已经抵达,ACK被丢了. 这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进⾏确认;

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

情况⼆:数据包就直接丢了.

• 当某⼀段报⽂段丢失之后,发送端会⼀直收到1001这样的ACK,就像是在提醒发送端"我想要的是 1001" ⼀样;

• 如果发送端主机连续三次收到了同样⼀个"1001"这样的应答,就会将对应的数据1001-2000重新 发送;

• 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就 已经收到了,被放到了接收端操作系统内核的接收缓冲区中;

这种机制被称为"⾼速重发控制"(也叫"快重传").

5. 流量控制

接收端处理数据的速度是有限的.如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送 端继续发送,就会造成丢包,继⽽引起丢包重传等等⼀系列连锁反应.

因此TCP⽀持根据接收端的处理能⼒,来决定发送端的发送速度.这个机制就叫做流量控制(Flow Control);

  • ·接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端;
  • ·窗口大小字段越大,说明网络的吞吐量越高;
  • 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
  • ·发送端接受到这个窗口之后,就会减慢自己的发送速度;
  • 如果接收端缓冲区满了,就会将窗口置为O;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端.

接收端如何把窗⼝⼤⼩告诉发送端呢?回忆我们的TCP⾸部中,有⼀个16位窗⼝字段,就是存放了窗⼝⼤⼩信息; 那么问题来了,16位数字最⼤表⽰65535,那么TCP窗⼝最⼤就是65535字节么?

实际上,TCP⾸部40字节选项中还包含了⼀个窗⼝扩⼤因⼦M,实际窗⼝⼤⼩是窗⼝字段的值左移M位;


6. 拥塞控制


拥塞控制是一种网络管理机制,旨在确保在网络中传输数据时不会造成拥塞,从而维持网络的稳定性和性能。它主要针对因网络拥塞而导致的数据丢失、延迟增加和网络性能下降等问题。拥塞控制通过动态调整数据流的发送速率来适应网络的状态,以便在不超出网络容量的情况下尽可能地利用网络资源。

拥塞控制通常在传输控制协议(TCP)中实现,它采用了几种经典的算法:

  1. 慢启动(Slow Start)在连接建立时,发送端会以指数增长的速率逐渐增加发送窗口的大小,从而迅速填充网络的带宽。这样可以有效利用网络资源,但如果拥塞发生,发送端将进入拥塞避免阶段。

  2. 拥塞避免(Congestion Avoidance)在慢启动阶段结束后,发送端将切换到拥塞避免算法,线性增长。在拥塞避免阶段,发送端逐渐增加发送窗口的大小,但增长速率较慢,以避免过快地占用网络资源,同时还能及时响应网络拥塞的情况。

  3. 快速重传(Fast Retransmit) :当发送端连续收到接收端的重复确认(duplicate acknowledgments)时,**表示可能有数据包丢失。发送端会立即重传丢失的数据包,**而不必等待超时发生。这可以快速恢复丢失的数据包,减少因丢包而导致的网络拥塞。

  4. 快速恢复(Fast Recovery) :在快速重传后,发送端将进入快速恢复状态,继续以慢速线性增加发送窗口的大小。这有助于发送端在检测到丢失的数据包后更快地恢复到正常发送速率,而不必完全回到慢启动阶段。

此外,拥塞控制还涉及到流量管理、队列管理等技术,以确保网络中的数据流能够按照一定的规则进行传输,从而避免因过多数据流导致的拥塞。

总的来说,拥塞控制是一种重要的网络管理机制,通过动态调整数据传输速率和窗口大小,以适应网络的状态变化,从而确保网络的稳定性和性能。

拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对⽅,但是⼜要避免给⽹络造成太⼤压⼒的折 中⽅案.

TCP拥塞控制这样的过程,就好像热恋的感觉


7. 延迟应答

也是基于滑动窗口,是要尽可能的再提高一点效率.

结合滑动窗口以及流量控制,能够通过延时应答ack的方式,把反馈的窗口大小,搞大一点.
核心就在于在允许的范围内,让窗口尽可能的大.
接收方收到数据之后不会立即返回ack,而是稍等一下.等一会再返回ack.
等了这一会,相当于给接收方的应用程序这里,腾出来更多的时间,来消费这里的数据.

如果接收数据的主机⽴刻返回ACK应答,这时候返回的窗⼝可能⽐较⼩.

• 假设接收端缓冲区为1M.⼀次收到了500K的数据;如果⽴刻应答,返回的窗⼝就是500K;

• 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;

• 在这种情况下,接收端处理还远没有达到⾃⼰的极限,即使窗⼝再放⼤⼀些,也能处理过来;

• 如果接收端稍微等⼀会再应答,⽐如等待200ms再应答,那么这个时候返回的窗⼝⼤⼩就是1M;

⼀定要记得,窗⼝越⼤,⽹络吞吐量就越⼤,传输效率就越⾼.我们的⽬标是在保证⽹络不拥塞的情况下 尽量提⾼传输效率;

那么所有的包都可以延迟应答么?

肯定也不是;

• 数量限制:每隔N个包就应答⼀次;

• 时间限制:超过最⼤延迟时间就应答⼀次;

具体的数量和超时时间,依操作系统不同也有差异;⼀般N取2,超时时间取200ms


8. 捎带应答

修改窗口大小,确实是提升效率的有效途径.

捎带应答,就是走另一条路,尽可能的把能合并的数据包进行合并,从而起到提高效率的效果.


9. 面向字节流

创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区;

  • ·调用write时,数据会先写入发送缓冲区中;
  • ·如果发送的字节数太长,会被拆分成多个TCP的数据包发出;
  • 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去;
  • ·接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区;·然后应用程序可以调用read从接收缓冲区拿数据;
  • 另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,
  • 也可以写数据.这个概念叫做全双工
  • 由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:
  • 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次read一个字节,重复100次;

粘包问题

粘包问题是在网络通信中常见的一种情况,通常发生在基于流式传输的协议(如TCP)中。它指的是发送方连续发送的数据包在接收方收到时被粘在一起,形成一个大的数据包,而不是按照发送方发送的边界进行分割。这可能会导致接收方无法正确解析数据包,从而造成数据解析错误或者应用程序逻辑错误。

粘包问题通常由以下几个原因引起:

  1. 发送方发送速度快于接收方处理速度:当发送方连续发送多个数据包时,如果接收方处理速度较慢,多个数据包可能会在接收方的缓冲区中被合并成一个大的数据包。

  2. 网络传输过程中的缓冲区大小限制:在网络传输过程中,数据包会经过多个网络设备和缓冲区。如果某个缓冲区的大小限制较小,而发送方发送的数据包大小超过了这个限制,那么多个数据包可能会在同一个缓冲区中被合并成一个。

  3. 操作系统内核对数据的处理方式:操作系统内核在接收数据时可能会对数据进行缓存,直到达到一定大小或者一定时间后再交给应用程序处理。这可能会导致多个数据包在操作系统内核中被合并成一个大的数据包。

解决粘包问题的方法有多种,其中一些常见的包括:

  1. 消息边界标记:在数据包中添加消息边界标记,接收方根据这些标记来正确解析数据包,确保每个数据包被正确分割。

  2. 固定长度消息:发送方将每个消息固定为相同的长度,接收方根据消息长度来正确解析数据包。

  3. 消息头部包含长度信息:在每个数据包的头部包含数据长度信息,接收方根据长度信息来正确解析数据包。

  4. 使用应用层协议:设计应用层协议时考虑粘包问题,采用适当的协议设计来避免粘包,如使用换行符、XML、JSON等格式化数据。

  5. 流量控制和流量调节:通过流量控制和流量调节等机制来控制发送方的发送速率,以使发送方发送的数据包与接收方处理的速率相匹配,避免粘包问题的发生。

通过以上方法,可以有效地解决或者减轻粘包问题,确保数据在网络通信中能够正确传输和解析。

对应的, UDP没有这个问题. UDP传输的基本单位是UDP数据报.在UDP这一层,就已经分开了.只要约定好,每个UDP数据报都只承载一个应用层数据包.就不需要额外的手段来进行区分了.


10. 异常情况

1. 进程崩溃

2. 正常关机

3. 突然断电

a)接收方断电 (利用 rst 复位报文段清理数据)

b)发送方断电

4. 网线断开

TCP/UDP对⽐

我们说了TCP是可靠连接,那么是不是TCP一定就优于UDP呢?TCP和UDP之间的优点和缺点,不能简单,绝对的进行比较

.TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;

.UDP用于对高速传输和实时性要求较高的通信领域,例如,早期的QQ,视频传输等.另外UDP可以用于广播;

归根结底,TCP和UDP都是程序员的工具,什么时机用,具体怎么用,还是要根据具体的需求场景去判定.

用UDP实现可靠传输(经典面试题 )

参考TCP的可靠性机制,在应用层实现类似的逻辑;例如:

·引入序列号,保证数据顺序;

·引入确认应答,确保对端收到了数据;

.引入超时重传,如果隔一段时间没有应答,就重发数据;


总结

TCP⼩结 为什么TCP这么复杂?因为要保证可靠性,同时⼜尽可能的提⾼性能.

可靠性:

  1. 校验和序列号(按序到达)
  2. 确认应答
  3. 超时重发
  4. 连接管理
  5. 流量控制
  6. 拥塞控制

提⾼性能:

  1. 滑动窗⼝
  2. 快速重传
  3. 延迟应答
  4. 捎带应答

其他:

• 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)

以上就是要介绍的TCP全部内容了,写了我几天的博客,辛苦坏我了。

博客不易,鹏鹏卖艺😭😭😭

大家点点赞 , 收藏 加 关注,让孩子开心开心吧 😭😭😭****后续我会酷酷更新更多学习笔记。

相关推荐
Swift社区3 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht3 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht3 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
吾日三省吾码4 小时前
JVM 性能调优
java
麻瓜也要学魔法4 小时前
链路状态路由协议-OSPF
网络
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
Estar.Lee4 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
傻啦嘿哟5 小时前
代理IP在后端开发中的应用与后端工程师的角色
网络·网络协议·tcp/ip
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python