猿创征文|网络原理——UDP/TCP协议

  • Post author:
  • Post category:其他


在这里插入图片描述

请添加图片描述

⭐️

前言

⭐️

本文主要介绍在TCP/IP的五层协议模型中,传输层的最常见的两个协议——UDP协议与TCP协议。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。下边我们来一起具体了解这两个协议。

🍉

博客主页:


🍁

【如风暖阳】

🍁


🍉

精品Java专栏

【JavaSE】



【备战蓝桥】


、【JavaEE初阶】



【MySQL】



【数据结构】



🍉

欢迎点赞

👍

收藏



留言评论

📝

私信必回哟

😁

🍉

本文由 【如风暖阳】 原创,首发于 CSDN

🙉

🍉

博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉

博客中涉及源码及博主日常练习代码均已上传

码云(gitee)



GitHub


请添加图片描述

请添加图片描述



🍅1.UDP协议

UDP的特点为无连接、不可靠传输、面向数据报、全双工。

  • 无连接:不需要进行连接就可以发送数据,通过UDP协议可以完成一个主机同时向多个主机发送消息。
  • 不可靠传输:发送端并不能知道接收端是否收到了消息。
  • 面向数据报:以数据包为单位进行传输。
  • 全双工:A和B可以同时向对方发送接收数据

UDP的报文格式如下图:

在这里插入图片描述

上图为教科书上呈现的格式,其实际格式如下图:

在这里插入图片描述

以下为对其报文中的一些组成的解释:

  • 源端口号:发送发的端口号
  • 目标端口号:接收方的端口号
  • 包长度(报文长度):表示一个UDP数据包的大小,单位为字节
  • 校验和:用来验证网络传输的数据是否正确

在上述UDP报文中,报文长度用两字节存储,其范围为0~65535,如果有一个较大的数据报需要传输时,包长度就超出了最大范围,无法表示一个比较大的数据报,于是就有了TCP这种协议来更好的处理这种情况。



🍅2.TCP协议



2.1 TCP头部格式

TCP头部格式如下图:

在这里插入图片描述


源端口号、目的端口号、校验和

:其作用与UDP中的作用相同


序列号

:在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。

用于解决网络包乱序问题


确认应答号

:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后,可以认为在这个序号以前的数据都已经被正常接收。

用于解决不丢包的问题


首部长度

:表示TCP报文长度,一共4比特,能表示0-15,单位为4字节,也就是说能表示TCP报头长度为0-60字节,如果不够用,还有后边的

保留位


控制位

(TCP报文的核心字段):

在这里插入图片描述

  • ACK:该位为1时,「确认应答」的字段变为有效,TCP规定除了最初建立连接时的SYN包之外该为必须设置为1.
  • RST:该位为1时,表示TCP连接中出现异常必须强制断开连接。
  • SYN:该位为1时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
  • FIN:该位为1时,表示今后不会再发送数据,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN位为1的TCP段。

其余报头中暂未提及的部分在文章后续慢慢介绍。

TCP的特点是有连接,可靠传输,面向字节流,全双工。

  • 有连接:⼀定是「⼀对⼀」才能连接
  • 可靠传输:发送端能够知道接收端是否收到了消息
  • 面向字节流:以字节流的方式进行传输,可以自行定义发送的字节多少,比较灵活
  • 全双工:A和B可以同时向对方发送接收数据

在TCP的这些核心机制中,除了

可靠传输

之外,其他性质都可以在代码中体现出来,虽然可靠传输不能直接在代码中体现出来,但是这个却是

TCP最核心

的机制,下边我们就来了解TCP都是通过怎样的方式来完成

可靠传输

的。



2.2 TCP可靠性实现机制



2.2.1 确认应答

该机制为保证可靠传输的核心机制。

在发送方发出去数据之后,在接受方接收到消息后返回给发送方一个

应答报文(ACK)

来表示自己已经收到了。

下边我们来通过我请女神吃牛排的例子来更好的理解这个问题:

在这里插入图片描述

但如果发了多条消息,可能就会出现后发先至的情况:

在这里插入图片描述

如果出现这种情况,将会引起不必要的误会,我们可以通过对消息进行编号的方法来解决上述问题:

在这里插入图片描述

上述的序号与确认序号便对应TCP报头中的序列号与确认应答号,通过编号的方式来针对哪个消息进行确认应答。

在这里插入图片描述

在实际的TCP传输中,并不是按“消息条数”来进行编号的,而是按照字节来进行编号

在这里插入图片描述

A给B发送了1000个字节,序号为1-1000,那么B给A返回的应答报文(ACK)就会带有一个确认序号——1001(表示小于1001的数据都被主机B收到了,接下来主机A应该从1001这个序号开始,往后进行传递)



2.2.2 超时重传

在网络一切正常的情况下,上述确认应答可以正常完成,但如果由于网络问题出现了丢包的问题,超时重传机制就要其效果了(超时重传是确认应答的补充)

在一个数据发送出去之后,一段时间过去并没有收到应答报文,这时发送方将会重新发送数据(当然不会无限制的重发,如果由于网络问题将会停止重发)。

触发重传的情况有以下两种:

一种是数据丢了,那么发送方在等待一段时间后重发就可以了。

在这里插入图片描述

另一种情况是ACK丢了,对方虽然收到了消息,但是我却收不到ACK,对于发送方的我来说,并不知道是哪种原因导致我没有收到ACK,所以我以最坏情况打算,就认为是对方没有收到数据,在一段时间过后我会重发一次。但这样对方实则会收到重复的消息,在TCP内部会进行去重操作,并返回一个和之前相同的ACK。

在这里插入图片描述

图解TCP/IP 超时重传示图:

在这里插入图片描述



2.2.3 连接管理(三次握手、四次挥手)



2.2.3.1 三次握手

TCP是面向连接的协议,所以使用TCP前必须先建立连接,而

建立连接是通过三次握手来进行的。


在这里插入图片描述

客户端和服务器之间通过三次交互,完成了建立的过程,“握手”是一个形象的比喻。

在上图中的两侧也表明了TCP的状态,我们需要主要了解的如下:


LISTEN

:表示服务器启动成功,端口绑定成功,随时可以有客户端来建立连接(手机开机,信号良好,随时可以有人给我打电话)


ESTABLISHED

:表示客户端已经连接成功,随时可以进行通信了(有人给我打电话我接听了,接下来就可以说话了)

通过三次握手,可以

检查当前网络的情况是否满足可靠传输的基本条件

,例如我们在给人打电话时,打通电话后第一句是“喂,可以听到吗”,如果接收方听到了会回一句“听到了,你能听到我说话吗”,这一句回复就说明了我的

麦克风



喇叭

都是正常的,我听到后会回复“可以听到”,这一句回复验证了接收方的

麦克风



喇叭

是正常的,这个过程就是在验证

通信双发的发送和接收能力是否都正常


那如果两次可以吗



两次意味着缺少最后一次,此时客户端这边发送接收能力是正常的,但是服务器这边是残缺的,服务器不知道自己的发送能力是否正常,也不知道客户端的接收能力是否正常,所以第三次握手不可少。


如果四次可以吗



在这里插入图片描述

四次意味着SYN和ACK需要分开传输,降低了效率



2.2.3.2 四次挥手

通过三次握手,就让客户端和服务器之间建立好了连接,建立好连接后,就需要占用一定的系统资源来保存连接相关的信息,如果连接断开,此时之前保存的连接信息就没有意义了,对应的空间也就可以释放了。

在这里插入图片描述

双方各自向对方发送了FIN(结束报文段)请求,并且各自给对方一个ACK确认报文。

在三次握手中,一定是客户端主动发起的,但在四次挥手中,可能是客户端主动发起,也可能是服务器主动发起。

三次握手中,中间两次可以合并,但是四次挥手的中间两次有时候合并不了(有时候是可以合并的),不能合并的原因在于B发送ACK和B发送FIN的时机不同,B给A发的ACK是由内核负责的,而B给A发的FIN是用户代码负责的(代码中调用了socket.close()方法),如果这两个操作之间的时间差比较大就不能合并了,如果时间差比较小是又可能合并的(延时应答和捎带应答,在后边详细解释)。

我们也来认识

四次挥手

中两个重要的状态:


CLOSE_WAIT

: 四次挥手挥了两次之后出现的状态,这个状态就是在等待代码中调用socket.close()方法,来进行后续的挥手过程


TIME_WAIT

:谁主动发起FIN,谁就会进入TIME_WAIT状态。其是为了给最后一次ACK提供重传机会。

由于最后一次主动方发送ACK后可能丢包,如果丢包了被动方就会以最坏情况,重传FIN(超时重传),所以主动方需要预留一段时间来等待被动方重发FIN,预留的时间为2MSL,MSL表示报文最大生存时间。如果在等待过程中没有接收到FIN,服务器就与客户端断开连接了。



2.2.4 滑动窗口

TCP虽然可靠性是最高的机制,但是TCP也会尽可能的提高传输效率

由于确认应答机制的存在,导致了当前每次执行一次发送操作,都需要等待上个ACK的到达,大量的时间都花在了等待ACK上了。

在这里插入图片描述

滑动窗口的本质就是在“批量的发送数据”,一次发送一波数据,然后一起等一波ACK

在这里插入图片描述

比如客户端一次发送了4组数据,然后等ACK的到达(这样一份的时间就等待了多份ACK,把多份ACK的时间压缩成一份了),如果一次批量发送数据为N,统一等待一波,此时这里的N称为“窗口大小”,“滑动”的意思是,并不用把N组数据的ACK都等到了才继续往下发送,而是收到一个ACK就往下发送一组。随着ACK接连到来,接连发送新的数据,此时这个“窗口”,就在逐渐往后“滑动”。

在这里插入图片描述

但如果在滑动窗口的背景下出现了丢包问题,该如何进行重传呢?

丢包分为两种情况:

一种是

ACK丢了

,这种情况不需要处理,这是因为ACK确认序号的含义是该序号以前的数据都已经收到了,也就是说在发送方收到5001的时候,意味着1-5000的数据都确认收到了,及时ACK(确认应答号)3001、4001都丢包了,只要收到5001,就涵盖了3001和4001表达的信息。

在这里插入图片描述

另一种情况是

数据丢了

,这就必须要进行处理了,如下图,由于1001-2000这个数据丢了,B就在反复索要1001这个确认应答号,即使A已经在给B发后边的数据了,但仍然再索要1001的确认应答报文,当索要若干次以后,A就会触发重传。

在这里插入图片描述

在A重传1001-2000之前,B的接收缓冲区如下图所示,数据一直在接收但是有缺口,在A完成重传后把接口给补上就行了(其他已经到了的数据就不必再进行重传),接下来B就向A索要7001开始的数据就行,这种机制也被称为高速重发控制。

在这里插入图片描述



2.2.5 流量控制

在滑动窗口中,窗口越大,传输效率就越高,但是我们不仅要考虑

发送方的传输速度

,还需要考虑

接收方的处理速度




流量控制

的关键,就是能够衡量接收方的处理速度,根据接收方

接收缓冲区的剩余空间大小

,来衡量当前的处理能力。

如果剩余空间比较大,就认为接收方的处理能力比较强,就可以让A发的快点

如果剩余空间比较小,就认为接收方的处理能力比较弱,就可以让A发的慢点

在TCP头部中,有窗口大小一栏,接收方在ACK报文中通过窗口大小来告知发送方剩余空间的大小。

在这里插入图片描述

《图解TCP/IP》流控制示意图:

在这里插入图片描述

其中的窗口探测报文中不含任何数据,只是为了触发ACK,来知道当前窗口的大小是多少。



2.2.6 拥塞控制

也是滑动窗口的延申,用于限制滑动窗口发送的速率

拥塞控制衡量的是

发送方到接收方的链路之间的拥堵情况(处理能力)


在这里插入图片描述

A能够发多快,不光取决于B的处理能力,也取决于中间链路的处理能力。

A一开始以一个比较小的窗口来发送数据,如果数据很流畅的就到达了,那就逐渐加大窗口大小;如果加大到一定程度之后,出现了丢包的情况(丢包意味着通信链路出现拥堵了),这个时候就需要减小窗口。


通过反复的增大/减小窗口

,就逐渐摸索到了一个比较合适的范围,拥塞窗口就在这个范围中不断变化,达到“动态平衡”。

拥塞窗口的具体变化如下图:

在这里插入图片描述



2.2.7 延时应答

延时应答相当于流量控制的延申,流量控制的目的是为了使发送方不要发送的太快,而延时应答在此基础上,希望窗口能更大一些。

在发送方询问接收方窗口大小的时候,不立即做出回答,而是稍等一下再回答,这样接收方又可以多处理一部分数据,窗口就又大一些,这个操作就是在有限的情况下,又尽可能的提高了一点传输速度。



2.2.8 捎带应答

捎带应答又是延时应答的延申。

因为延迟应答的存在,导致接受方的ACK不一定是即时返回的,如果延时应答导致ACK的返回时机和应用代码中返回的响应时机重合了,就可以把这个ACK和响应数据合二为一(就像四次挥手中第二三次挥手过程,ACK和FIN就有可能同一时机返回)



2.2.9 粘包问题

TCP粘包粘的是应用层数据包(不仅是TCP存在粘包问题,其他面向字节流的机制比如读文件,也存在粘包问题)

在TCP接受缓冲区中,若干个应用层数据包混在一起就分不出来谁是谁了,如果想要解决这个问题就需要在应用层中加入包的边界,比如:约定每个包的结尾以;结尾。



2.2.10 TCP异常处理

1.

进程终止


在进程毫无准备的情况下,突然结束进程。

在这里插入图片描述

TCP连接是通过socket来建立的,socket本质上是进程打开的一个文件,文件其实就存在于进程PCB里的文件描述符表

每次打开一个文件(包括socket),都会在文件描述符表里增加一项

每次关闭一个文都会在文件描述符表里删除一项

如果直接杀死进程,PCB也就没有了,里面的文件描述符表也就没有了,此处文件相当于“自动关闭”了,这个过程和手动调用socket.close()一样,都会触发四次挥手

2.

机器关机


如果按照操作系统约定的正常流程关机,会让操作系统杀死所有进程然后关机,四次挥手的过程依旧会执行。

3.

机器断电/网线断开

当电源或者网线直接断开时,操作系统根本来不及反应,那么四次挥手也无法执行

当接收方断电或者断网时,发送方会尝试重新连接,重连失败一定次数,就会放弃连接。

当发送方断电或者断网时,接收方会发送一个探测报文,触发发送方的ACK,如果没有反应,接收方就认为发送方出现了问题。



🍅3.TCP/UDP总结

如果开发对可靠性有一定要求时,使用TCP(日常开发中的大多数情况都是基于TCP);如果开发对可靠性要求不高,对于效率要求更高,使用UDP(机房内部的主机之间的通信)

UDP如何实现可靠性呢?其实UDP实现可靠性是TCP的复刻,只是按照相同的思路在应用层完成就可以了。


⭐️

最后的话

⭐️


总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

请添加图片描述



版权声明:本文为qq_60856948原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。