UDP + TCP协议详解

UDP协议

UDP(User Datagram Protocol)是一种面向无连接的传输层协议。与TCP(Transmission Control Protocol)相比,UDP提供了一种简单、轻量级和低延迟的数据传输机制。

1:UDP的特点

1:无连接

即发送方和接收方无需建立连接

2:不可靠

没有像TCP那样的确认机制、重传机制和流量控制机制。这意味着在数据传输过程中,可能会发生丢包、数据损坏或乱序的情况。

3:面向数据报

UDP以数据报文(数据包)为单位进行传输。

4:全双工

通信双方都可以通过DatagramPacket进行发送和接受数据报

5.简单和轻量级

2:报文结构

*1.检验和:udp检验和提供了差错控制功能,用于确定当UDP报文段从源到目的移动时,其中的比特是否发生了改变。UDP的检验通过CRC校验算法来实现。

但是。虽然UDP提供了差错检验,但是它对差错恢复是无能为力的,对出现差错的报文UDP一种处理方法是直接丢弃受损的报文段,另一种做法是:将受损的报文段交给应用程序并给出警告。

*2.长度:报头中“长度”部分规定了udp报文的长度:2个字节->16个比特位->表示的数据范围:0 - 65535

大概就是64kb,这对于我们现在的应用场景来说(开发这个协议的年代64kb很大,大佬们没有想到互联网发展的这么快),实在是很小。

这个时候就产生了一个问题,我们要传输的数据单位大于64kbl咋办:

                第一种解决方案:拆分和重组。  但是其中涉及到很多细节,比如如何拆分,怎么重组,顺序乱了咋办,丢包了怎么处理等等,成本很高。

                第二种解决方案:使用对数据长度更宽容的协议,如TCP。

*3 端口号  端口号的大小也是2个字节,即端口号的范围是:0 - 65535     但是,0 - 1023是知名端口号(公认端口),我们一般不能使用。

TCP协议

TCP(Transmission Control Protocol)是一种面向连接的传输层协议。它提供了可靠的、有序的、全双工的字节流传输,用于在计算机网络上可靠地传输数据。

TCP的特点

1.有连接

连接管理:

建立连接:三次握手

第一次握手(SYN):客户端发送一个SYN(同步)报文段给服务器,(三次握手第一次发起方一般是服务器)请求建立连接。该报文段包含一个随机的初始序列号(ISN)作为起始点。

第二次握手(SYN+ACK):服务器收到客户端的SYN报文段后,会发送一个SYN+ACK报文段作为响应。该报文段中,确认号(ACK)设置为客户端的初始序列号加1,同时也包含一个随机的初始序列号(ISN)作为服务器端的起始点。

第三次握手(ACK):客户端收到服务器的SYN+ACK报文段后,会发送一个确认报文段(ACK)给服务器。该报文段的确认号(ack)设置为服务器的初始序列号加1,表示客户端已经收到了服务器的响应。

.为什么要进行三次握手?

1. “投石问路”,验证网络通信是否通畅以及每个主机的功能是否正常(“三次”是通信双方都知晓双方发送和接收能力的最少次数)。

2. “消息协商”,协商建立连接和通信必要的参数。

比如每一端连接的初始序号一般都与双方上一次连接的序号有较大差异,这就需要通过双方在三次握手期间进行协商来确定。

断开连接:四次挥手

1.为什么不能像建立连接过程一样将第二步和第三步合并起来?

SYN结束报文触发是由用户代码来控制的:调用close方法或者进程结束。而ACK应答报文由系统内核控制,如果我们忘记进行close或者要执行一些收尾工作(close执行时机会很慢),对于大部分情况,这俩步执行时间会有很大差异,所以不能写在一起。

这个时候就会有一个问题:被动关闭连接的一方如果迟迟不发送FIN结束报文怎么办(比如写出bug,没有调用close方法)?

当主机收到FIN结束报文起,其就会进入CLOSE_WAIT状态,这时主机针对socket对象可以进行读操作,不能进行写操作,即当前连接以及无法正常使用了。而关闭连接的请求方会等待一段时间,如果还收不到FIN结束报文,就会单方面关闭连接。

2.四次挥手期间出现丢包怎么办?

三次握手和四次挥手期间仍然有超时重传机制。如果在重传多次后还是没有成功,此时仍然会单方面释放连接。

这个时候就会存在一个问题,我们以连接释放请求方(主机A)的角度来看,在A收到对方的FIN并且发送ACK应答报文后在四次挥手期间A的任务已经完成了,完成了就立刻断开连接吗?可是如果发送的ACK丢失了怎么办?

ACK丢失了主机B就会重新发一个FIN结束报文,结束报文发出来一看:连接没了。。。。它很懵逼。

所以在连接释放请求方收到对方的FIN后不能立刻断开连接,还需要等待一段时间,来预防自己的ACK出现丢包的情况。这个等待时间为2 * MSL(MSL是TCP协议中的一个参数,全称为Maximum Segment Lifetime,表示TCP数据报文在网络中的最长生存时间。)

极端情况:在这个等待时间内,发送方ACK丢失且接收方重传的FIN丢失,则说明当前网络状态极差,不具备可靠传输条件,因此,单方面释放连接也就无所谓了。

2.可靠传输

TCP实现可靠传输的方式:确认应答

确认应答机制:当发送端向接收端发送数据时,接收端需要向发送端发送确认应答,表示已经收到了数据。    确认应答机制是保证TCP传输可靠性的最核心的机制。    

具体的确认应答方式:

给数据(每个字节)进行编号(初始序号是随机产生的,目的是为了防止网络攻击),每次进行确认应答时发送收到的数据的下一个编号表示接下来期望收到的数据:

如图所示:主机A像主机B发送编号1 - 1000的数据报,主机B向主机A发送编号为·1001的确认应答表示:1001之前的数据我都收到了,我期望你下次从1001开始发送。   另外,在主机A数据报中,第一个字节的序列号即保存在其报头的“序号”模块中,其确认应答的序号保存在报头的”确认序号“模块中,基于此,我们就可以通过”序号“和”确认序号“计算出数据的长度:确认序号 -  序号。

为什么要对数据进行排序?

1.针对“后发先至”的情况

我们的计算机网络是冗余式的,同一个出发点和目的地,之间通过很多条道路相连,并且每条路径上的路由器/交换机的繁忙程度也是不一样的,所以非常容易产生“后发先至”的情况。

应答报文产生“后发先至”的情况:

引入到确认应答的场景当中,应答报文产生错序就会导致主机对这是哪个报文的应答产生错误判断,进而引发各种问题。

2.便于在接收方缓冲区对数据报后发先至导致的错序的数据报进行排序

3.对于超时重传的数据报进行去重

4.让接收方对丢失的数据报做出及时的反映

超时重传:

超时重传是对确认应答机制的一个重要补充,当发送方迟迟收不到应当报文时,超时重传就要上线了。而发送方收不到应答报文大致分为俩种情况:

1:包丢了(数据包丢失或者应答报文丢失)

2:接收方未能及时处理(网络拥塞或者接收方负载过高)

而发送方并不能确定具体收不到应答报文的原因是什么,所以他会一视同仁,统一进行超时重传的处理。

这个时候就会产生一个问题:接收方会收到很多重复的报文。   这个时候我们序号的作用就体现出来了:接收方会维护一个“接收缓冲区”。接收缓冲区用于临时存储从网络中接收到的数据包。它以队列形式存储数据包,并提供给应用程序读取和处理。接收缓冲区中数据报按顺序进行排列,所以接收方就会很容易判断出这个数据包是否已经收到过,如果收到过,接收方就会丢弃这个重复报文并向发送方发送一个确认应答报文,告诉发送方该数据包已经成功接收。重复确认应答报文的确认号设置为已成功接收的最后一个字节的序列号,而不是重复数据包的序列号。

超时重传中时间是多久呢?

最理想的情况下,找到一个最小的时间,保证 "确认应答一定能在这个时间内返回"。

但是这个时间的长短,随着网络环境的不同,是有差异的。

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

如果超时时间设的太短,有可能会频繁发送重复的包;

另外,我们要明确:丢包是一个小概率事件,连续传输又丢包更是很小的概率,这个时候大概率就是网络出现故障了。此时,频繁进行重传就是无用之功,所以等待时间是会逐渐拉长的。并且,发送方并不会无限的进行重发操作,当重发达到一定的次数后,这个时候发送方就会认为连接出了问题,就会放弃发送,尝试进行”重置TCP连接“:发送重置报文(将报头的RST设置为1)。如果重置报文在等待时间内仍然没有应答,发送方就会关闭当前连接并通知应用程序。

3.面向字节流

我们在之前使用TCP套接字时就有深刻体会:使用socket在字节流中写入/读取数据

产生的问题:

粘包问题

在TCP眼里,我的任务就是把这些应用层发送下来的字节成功转发到另一端就好了,并不关心这些字节是否存在一定的格式是否是一个数据包。

TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方

接收方原因:

TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

发送方原因:

为了提高网络的效率,通常会将多个数据报文组合成一个TCP数据包进行发送。

解决方法:

在传输层中已经无解了。因为传输层并不知道应用层发送的数据的具体格式和内容(面向字节流),也就无法作出区分。

但是在应用层还有得救:

1:在应用层协议中引入分隔符

2:在应用层协议中引入每个数据包的长度

4.全双工

提升传输效率的方式:滑动窗口

由于TCP确认应答机制的存在,导致在传输中相当一部分时间都消耗在等待确认应答上了,传输效率不尽如人意。而滑动窗口就是在保证TCP可靠传输的前提下,尽可能缩短这个等待确认应答的时间,以提升TCP传输效率。

引入滑动窗口机制后,发送方维护了一个发送缓冲区(窗口),发送缓冲区中是无需等待确认应答而可以继续发送的数据。

在上图中,主机A维护了一个大小为4000的窗口,分为4个字段,发送前四个段的时候,不需要等待任何ACK,直接发送; 收到第一个ACK后,滑动窗口向后移动(将收到ACK的数据段从缓冲区中删掉),继续发送第五个段的数据(将第五段的数据引入缓冲区);依次类推;

引入滑动窗口机制后丢包的处理方法

1:ACK丢失

在引入滑动窗口机制后,系统会采用累计应答的方式,即一个数据报的ACK不仅表示这个数据报被接收还表示这个数据报之前的数据报被成功接收。

比如在上图过程中,虽然字节1-1000的数据报的ACK丢失,但是主机A接收到字节1001-2000的数据报的ACK,就明白1-2000字节的数据以及被主机B成功接收了,我继续往后发送就行了。即对于ACK丢失双方不用进行任何操作也能正常进行数据传输(除非所有ACK丢包)。这种机制被称为累计应答机制。

 

2:数据包丢失

当某一段数据报丢失后,如果发送方继续发送后面的数据包,接收方就会重复发送丢失数据包的ACK。如果发送方连续收到多次重复的确认消息,发送方就明白这个数据报丢失了会立即重发这个丢失的数据报。在这期间发送方发送的其他数据报,接收方会将这些数据报保存在接收缓冲区当中并按顺序排列,并空缺出丢失数据报的位置。当发送方重传丢失数据报后接收方开始按序读取这些数据报,下一次ACK就会直接发送缓冲区最后一个数据报的ACK。

比如在上图过程中,主机A字节1001-2000的数据报丢失了,当主机A再发送之后的数据报时,主机B发送字节1001 - 2000数据的ACK,主机B进行3次的重复ACK后,主机A重传1001- 2000的数据报,这期间主机A发送了2001- 7000的数据报,所以主机B下次就会直接发送ack为7001的应答报文。

流量控制:

前面提到过:发送方维护了一个发送缓冲区(窗口),发送缓冲区中是无需等待确认应答而可以继续发送的数据。

这也就意味着,窗口越大,传输速率越高,但是,窗口也不能太大,窗口太大就会造成接收方或者中间的节点处理不过来的情况,处理不过来就会产生丢包,产生丢包就要重传,反而影响效率。所以,这个窗口设置多大,就是一门学问。

流量控制就是根据接收方的处理能力来调整发送方窗口的大小。

如何衡量接收方的处理能力?    使用接收方缓冲区剩余空间的大小作为衡量的标准。

所以,我们的ACK应答报文(报头的ACK模块为1)就会将接收方当前的窗口大小保存在报头的“窗口大小”模块(所以这一模块当报文为应答报文时才生效)。发送方会根据接收方缓冲区剩余空间的大小动态的调整自己的窗口大小。

接收方缓冲区满的情况:

如果接收方缓冲区满了,接收方就会通过ACK通知发送方,此时发送方就会停止发送。但是发送方也不能干等,此时发送方间断性发送“窗口探测数据包“,窗口探测包不携带数据,只是为了触发接收方发送ACK,以此来了解此时接收方缓冲区情况来判断此时是否能发送数据报了。

通过流量控制,TCP协议可以避免网络拥塞和数据丢失,确保数据传输的稳定性和可靠性。

拥塞控制

在主机之间通信传输过程中,整个链路的传输速率除了与主机的发送/接收能力有关,还与中间环节(如路由器/交换机的转发能力)有关。这些中间环节的薄弱部分往往限制了我们的传输速率(木桶效应),造成网络拥塞的情况。TCP采用让每一个发送方感知网络拥塞程度来限制其向连接的发送速率来解决这一问题:

        如果发送方感知到路径上有阻塞(出现丢包现象)就会降低其发送速率(减小窗口大小)

        如果发送方感知到路径通畅(没有丢包现象)就会增加其发送速率(扩大窗口大小)。

改变窗口大小的规则(此处我们引入拥塞窗口的概念(cwnd)):

实际发送方的窗口 = min(拥塞窗口,流量控制窗口)

        1.慢启动:

慢启动的规则就是当发送方收到一个ACK,拥塞窗口的大小就会加1。

数据传输开始时,我们并不清楚整个网络的情况,所以将拥塞窗口设为1。

之后每收到一个ACK拥塞窗口大小翻倍,拥塞窗口呈现一个指数增加的状态。

指数增长的速度是很快的,我们要加以限制,防止网络突然陷入严重的拥塞:什么适合结束这种指数增加状态?

设置一个拥塞窗口门限值ssthresh(slow start threshold)状态变量

当拥塞窗口  < 门限值 时,采用慢启动算法

当拥塞窗口 > 门限值 时,开始拥塞避免算法

        2.拥塞避免

规则:每收到一个ACK报文,拥塞窗口cwnd增加1

当指数增长让拥塞窗口到达阈值之后,开始进行线性增长(这时出现拥塞的可能已经非常大了,所以我们要线性增长慢慢接近) 

拥塞避免算法什么适合停止:网络中出现延时、丢包现象,导致TCP发生重传,此时就会进入拥塞发生阶段。

        3.拥塞发生

导致TCP出现重传主要有俩种原因:

1:快速重传:连续3个重复的ACK应答报文

2:超时重传

        快速重传处理方法:

  • cwnd(拥塞窗口) = cwnd/2,拥塞窗口减少为一半
  • ssthresh = cwnd
  • 进入快速恢复算法

        超时重传的处理方法:

  • 拥塞窗口设置为1,门限值设置为拥塞发生时拥塞窗口的一半
  • 迁移到慢启动阶段。

        4.快速恢复

  1. 拥塞窗口cwnd = ssthresh + 3(表示有三个数据包被收到)
  2. 重传丢失的数据包
  3. 如果收到的ACK是重复的,cwnd增加1
  4. 如果收到新数据的ACK,把拥塞窗口设置为第1步中ssthresh的值,因为ACK已经确认了新数据,快速恢复过程可以结束,可以再次进入拥塞避免阶段

拥塞窗口和流量控制共同限制了滑动窗口,使滑动窗口在保证可靠性的前提下,尽可能提升传输效率

延迟应答:

之前提到过,ACK报文会携带接收端缓冲区剩余空间大小以供发送方调整自己的窗口大小(流量控制)。延迟应答就是尽量延缓接收方发送ACK应答报文的时间,让接收方尽量“消化掉”缓冲区的报文,以使缓冲区有更大的空余空间,这样发送方有更大的窗口,进而有更大的吞吐量和传输效率。

限制

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

时间限制:超过最大延迟时间就应答一次

捎带应答

在延迟应答的基础上,合并ACK应答报文和普通的数据报文。

能合并的原因:1:时机上可以实现二者的并发

                         2:ACK应答报文不需要携带载荷,与正常数据不冲突。

此时,TCP报头的确认应答号模块会同时保存数据报的期望应答号和合并的ACK报文的响应号。

异常情况的处理:

1.进程崩溃

进程没了 ---   PCB没了 --- 文件描述符表没了 --- socket文件没了 --- 相当于调用socket.close()方法 --- 触发四次挥手机制

与正常结束无异

2.主机关机

关机过程中,就是一个关闭主机进程的过程,此时的情况与第一种情形无异。一般情况下,关机过程中足够进行四次挥手了,连接正常关闭。

如果没有进行完,及在四次挥手期间一方掉线无回应,这是的处理方法在四次挥手板块已经讲过,及进行重传,多次重传未果后单方面释放连接。

3.主机掉电

主机掉电是一个瞬间的事,是来不及进行反应的。及进行不了四次挥手的操作,发送不了任何FIN结束报文。这时分为俩种情况:

       1. A向B发送消息,A掉电:

此处引入心跳包的概念:

在计算机网络中,心跳包(Heartbeat)是指网络设备或应用程序周期性地发送一些数据包来维持连接和状态的一种机制。通常情况下,心跳包是空的数据包,只包含一些固定的标识信息,如设备名称、IP地址、时间戳等。

心跳包的作用是确保网络设备或应用程序处于正常运行状态,以及检测连接是否中断或出现故障。当接收方收到心跳包时,它会向发送方发送一个确认包,表示连接正常。如果发送方在一定时间内没有收到确认包,就会认为连接已经断开或出现故障,需要进行相应的处理。

明白心跳包后,这个问题也就迎刃而解了。    

      2.  A向B发送消息,B掉电:此时A向B发送消息,就会等待B的ACK回应报文,等不到就会进行超时重传,当重发达到一定的次数后,这个时候发送方就会认为连接出了问题,就会放弃发送,尝试进行”重置TCP连接“:发送重置报文(将报头的RST设置为1)。如果重置报文在等待时间内仍然没有应答,发送方就会关闭当前连接并通知应用程序(超时重传板块)。

4.网线断开

与第三种主机掉电的情况无异。


报文结构

端口号:表示应用程序的的地址

序号:顺序号,4个字节,用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个字节的顺序号。当建立一个新的连接时,即 SYN 标志变 1 ,序号字段包含由这个主机选择的该连接的初始顺序号 ISN ( Initial Sequence Number )(三次握手进行信息交互商议产生)。

确认序号:包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据最后一个字节的编号加 1 。只有 ACK 标志为 1 时确认序号字段才有效。   

首部长度:TCP报头长度是可变的(选项部分的长度不固定),所以需要这个字段来表示报头的长度。这里虽然只占4个bit位,但是单位是4字节,所以最大长度是60个字节。

保留区域:给将来进行功能扩展来使用,目前还没用,必须全部置为0。

窗口:窗口大小字段表示接收方的接收缓冲区可接收的字节数量。发送方根据窗口大小来控制发送数据的量,以避免过载或拥塞。(流量控制)窗口大小是一个 16bit 字段,因而窗口大小最大为 65535(2^16 - 1)。

六个控制位:

  • URG(Urgent):表示紧急指针字段是否有效。
  • ACK(Acknowledgment):表示确认序号字段是否有效。即表示应答报文。
  • PSH(Push):请求立即将数据推送给应用程序。
  • RST(Reset):重置连接。
  • SYN(Synchronize):用于建立连接。(三次握手期间)
  • FIN(Finish):表示发送方已完成数据发送。(四次挥手期间)

检验和:校验和字段用于检测TCP报文的完整性。发送方计算校验和,并将其包含在报头中。接收方在接收到报文后重新计算校验和,与报头中的校验和进行比较,以检测是否有错误或损坏。

紧急指针 (Urgent Pointer): 紧急指针字段仅在URG标志位设置时有效。它指示紧急数据的结束位置,在报文中的字节偏移量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值