Linux:UDP与TCP

1.UDP

前置问题:
1.一个进程是否可以绑定多个端口号?

可以,通过创建不同socket

2.一个端口号是否可以bind到不同进程?

通常不能,需要保证网络资源的唯一性,不过父子进程会共享socket


UDP协议端格式:UDP报头

除了这四个字段还可能有数据(报文)

字段含义:端口号都为16位

source:源端口号 dest:目的端口号 check:检验和(检测数据是否有传输错误)

len:发送方填写的理论UDP长度(UDP首部+UDP数据)

UDP传输特点:

1.无连接:知道对端套接字可以直接开始传输,不用建立连接

2.不可靠:没有确认和重传机制

3.面向数据报:不能灵活控制读写数据次数和数量

注意:

UDP的socket即可读也可写,所以叫全双工


1.报头如何和有效载荷分离?

因为udp的报头固定占用8字节,所以固定读取前8字节即为报头,剩下的为报文

2.分用问题,如何交给上层,上层如何返回?

通过目的端口号知道上层具体位置,交给上层;上层知道源端口号从而返回给当前层

3.什么是面向数据报?

发送端进行一次发送,接收端就进行一次接受(不会分n次小段接受数据),发送次数和接收次数是完全一致的

4.为什么说UDP是面向数据报的?

接收端接收后不考虑粘包问题,因为UDP底层已经考虑过

(根据报头的len字段和实际长度,判断报文是否完整,不完整直接丢弃)

5.UDP的缓冲区有什么注意点?

该缓冲区机制为:接受来不及读取的数据报,但是不保证存储顺序和发送顺序一致,且在缓冲区满了之后直接丢弃后面来的数据报

6.UDP的最大传输长度为64kb,如果需要传输的数据报大于64kb,如何传输?

只能手动分包,多次发送数据报,然后接收端手动组合


报文:底层使用结构体进行管理

struct sk_buff:描述系统层面一个报文的内存空间布局

与报头不同,报头描述的是报文的网络属性

我们所说的将数据报从传输层不断向下交付到链路层的过程,其实就是data指针依次减去TCP协议头占用字节,IP协议头占用字节,从而向上移动修改协议头内容

2.TCP

(1)TCP协议格式:

该协议同样也是利用内核级别结构体构建的,其中为了传输效率,使用了位段的知识

疑问1:如何分离报头和有效载荷?

由于TCP报头固定为20字节,所以先判断报文是否大于20字节,若大于就直接提取前20字节的报头信息;

由于报头可能携带选项,所以报头中有记录带上选项的完整报头的字节数,利用4位首部长度记录,单位为4字节

eg:二进制4bit位[0000]~[1111]->十进制范围[0,15]->乘上单位范围为[0,60]字节

最终由于报头至少为20字节,所以最终范围[20,60]

疑问2:TCP为什么不用关注报文总长度?

因为TCP传输不保证报文全部完整传输,他是面向字节流的

(2)TCP报头

**TCP的确认机制:**当一方发消息之后,对方接受之后一定要发送应答消息,从而100%确认发送方的消息已经被接收(不需要对应答进行应答,应答只是通信冗余信息),用于保证历史消息的可靠性

1.序号与确认序号:

发送方发送的是完整报文,接收方发送的应答只有报头

由于报文的送达顺序不是确定的,所以为了接收方可以正确识别发送方发出的报文顺序,报文中会携带序号,用来表示报文的发送顺序先后(数字小表示先)

而发送方为了能够确认接收方当前正确接收到哪个报文,报文中还会携带确认序号,用来表示当前序号之前的报文都被正确接收了

**注意:**实际上,应答不一定是单纯的只有报头,很多时候他是捎带应答(完整报文:包含这次需要发送的有效载荷以及对历史报文的确认序号)

2.窗口大小:

发送数据的时候如果数据量过大,接收端缓冲区存不下,就会丢包处理,此时是有资源浪费的,为了让发送端知道对端接收缓冲区的大小,我们利用TCP报头中的协议字段记录接收端窗口大小,从而根据该数据调整数据发送速率,进行流量控制

注意:报头中的窗口大小都是记录自己的接受缓冲区剩余空间

如果剩余空间较大,还可以让发送端发送更多数据,提高发送速率

所以窗口大小的流量控制不仅可以保证可靠性,也可以控制效率

3.标志位:

标志位是位段中的bit位,用来区分报文类型(发送,应答...)

六个标志位的含义:

**ACK:**表示确认号是否有效(属于确认报文,通常也是捎带应答,所以一般情况下都置为1

**SYN:**表示请求建立连接(属于同步报文段),应用于TCP建立连接前的三次握手(SYN->SYN+ACK->ACK)

**FIN:**通知对端,本端需要退出链接(属于结束报文段),应用于TCP断开连接点的四次挥手(FIN->ACK->FIN->ACK)

PSH:提示接收端立刻将缓冲区数据提取走

RST:出现链接异常的时候,接收端设置该标志位为1,让发送端重置连接

URG:用于标识数据为缓冲区中插队数据,让对端优先接受处理,可以用于终止传输等场景

注意:三次握手和三次挥手的本质都是四次握手与挥手,只是部分情况下对端进行了捎带应答,从而减少了一次发送


(3)超时重传机制:

约定一个时间,需要在规定时间内返回并确认应答,否则就判断超时,重发数据

疑问:我们可以确认应答超时是因为应答丢失还是数据发送就丢失了吗?

不能,因为两种情况都会导致应答返回超过规定时间

疑问:如何设置超时重传的时长?

如果设置的时长太长,会导致效率降低,设置太短会导致频繁重传

而网络环境是实时波动的,所以重传时长是动态计算的

(4)连接管理机制:

握手阶段:

SYN_SENT和SYN_RCVD分别是表示连接发送完成和链接接受完成的宏,ESTABLISHED表示握手完成的宏

疑问:为什么TCP是三次握手?

因为这是确认cs双方全双工的最少次数,第一次握手保证c端可以发送,第二次握手保证s端可以接收,第三次握手保证c端可以接收,s端可以发送

以及一般服务器默认接受所有客户端消息,所以将s端的SYN和ACK合在一起了,进行了捎带应答

挥手阶段:

疑问:当出现大量CLOSE_WAIT时,是什么原因导致的?

一般是服务器端(被动接收端)在知道主动发送端的FIN标志之后,没有调用close()接口关闭套接字

疑问:为什么TCP是四次挥手?

TCP是全双工的,两个方向的关闭是独立的

服务器端可能在客户端请求连接断开的时候还有数据没有发送,所以大部分情况不能立刻返回断开标志

疑问:主动断开的一方,四次挥手已经完成,为什么不能主动退出,而是要等待一段时间,让自己处于TIME_WAIT状态?

需要等待2倍的报文最大生存时间,为了防止迟到的数据包影响后续新连接(确保残存数据消亡),并确保被动的关闭方可以正确完成链接终止

(5)流量控制

TCP会根据接收端的数据处理能力来控制发送端的发送速率,以此实现流量控制

具体流程:

接收端将自己缓冲区大小放入报头的窗口大小字段中,通过ACK告知发送端,当窗口大小变大,发送端发送速率也变大,反之变小;当接收端的缓冲区满之后,将窗口大小设置为0,此时发送端就不会再发送数据,不过会隔固定时间发送窗口探测数据段,得知当前的窗口大小

疑问:第一次发送数据的时候,发送端是如何知道接收端的窗口大小的?

在建立连接的三次握手过程中,虽然没有发送有效载荷,但是有进行报头交换,所以在三次握手的时候就得知了接收端的窗口大小

疑问:窗口大小只有16位,所以缓冲区的上限就是2^16字节吗?

不是,因为在选项中有一个窗口扩大因子M,实际的缓冲区上限就是窗口大小左移M位

(6)滑动窗口

前面提到的发送方式,发送一个数据段之后要等待对端的应答被接收之后才发送下一个数据段,这会导致性能较差,为了解决它,我们可以一次性发送多个数据段(之间不用接收到应答才发送下一个数据段)

这里滑动窗口的大小就是4000字节

**滑动窗口大小:**暂时无需确认应答就可以继续发送数据的最大值

滑动窗口所处位置:发送缓冲区中,属于发送缓冲区一部分

滑动过程:

第一次先将滑动窗口内的所有数据段都依次发送出去,然后在接受到第一个应答的时候滑动窗口右移,发送当前窗口的最右端数据段,依次进行

滑动窗口把发送缓冲区分成了三部分:

1.左侧:已发送,已确认的数据段(空间可用)

2.中间:已发送,未确认的数据段

3.右侧:未发送,未确认的数据段

疑问1:滑动窗口的大小由谁确定?

由对方返回的ACK报头中窗口大小决定,利用滑动窗口实现流量控制

疑问2:滑动窗口的大小是否会变?什么情况变?

由于窗口大小是由对端接受能力决定的,所以窗口大小一定会变

当对方取数据速度变慢,接受缓冲区变小,滑动窗口变小(可能变为0)

当对方读取速度不变,接受缓冲区不变,滑动窗口不变

当读取速度变快,接受缓冲区变大,滑动窗口变大

滑动窗口发送方式出现丢包,如何重传?

此时可以通过后续ACK确认前面的数据正确到达

如果因为数据丢失导致出现三次重复确认应答,会对重复ACK的一个数据段进行重发,这叫快重传

疑问:超时重传和快重传是否冲突?
不会,他们是互补的,当滑动窗口大小较小的时候,可能不会出现三次重复确认应答,此时就要依赖超时重传机制

疑问:滑动窗口丢包分什么情况?
主要分三种:最左丢包,中间丢包,最右丢包

最左情况,左侧窗口指针start不移动,通过快重传或超时重传重新发送报文

中间情况,将左侧成功发送与应答的报文移出窗口(即窗口右移若干位),然后转换为最左解决方案

最右情况,和中间情况类似,窗口移动若干位后,启用最左方案

疑问:滑动窗口会越界吗?

不会,因为可以看成是环形队列,最末端划走后就重新启用最前端的存储空间

(7)拥塞控制

少量丢包可以通过重传解决,大量丢包就需要拥塞控制
疑问:为什么大量丢包一定不能利用重传解决?

因为一个网络中有若干对C-S端,当他们也大量丢包时,如果一起使用重传,会加剧网络拥塞

基本概念:
**1.拥塞窗口:**是一个int数据,用于控制当前网络允许发送的数据量

**2.概念更新:**滑动窗口=min(拥塞窗口,对端ACK的win大小),因为发送数据时既要考虑网络情况,也要考虑接收端情况

3.慢启动拥塞控制: 让拥塞窗口从1开始以指数级进行增长,发送数据量前期少,后期快速变多

在拥塞窗口小于慢启动阈值时,呈指数增长,当超过阈值时,为了避免无限制增长,此时呈线性增长且遇到网络拥塞时会将慢启动阈值更改为当前拥塞窗口值的一半

若快重传,慢启动阈值会变为原来一半,但是拥塞窗口不会置为一

若超时重发,慢启动阈值会变为原来一半,拥塞窗口变为1

解决方案:

当判断处于拥塞时,利用慢启动的拥塞控制算法进行发送数据量的控制

(8)延迟应答

主要目的是提高发送方的单次发送数据量,从而提高效率

流程:

我们在接收到发送方的数据之后,等待一段时间,这段时间内让接收端可以多提取一些数据,然后再返回ACK,此时由于我们给接收端多了一些时间提取数据,所以窗口大小会更大,发送方下次发送的数据也会变多

(9)粘包问题

在TCP中,由于我们的传输是面向字节流的,不会保证每次发送的数据都是一个完整报文,所以会出现粘包问题

解决方法:

1.对于定长报文:每次解析的时候都读取固定大小的数据

2.对于变长报文:可以在报头中的字段中保存报文的总长度,从而知道报文的结束位置

也可以用特殊字符来对不同报文进行分隔

(10)TCP异常情况

**进程终止/机器重启:**进程终止会释放文件描述符fd,相当于调用了close,申请断开连接,此时被动断开的一方进入CLOSE_WAIT状态

**机器掉电/网线断开:**此时对端仍然认为连接存在,但是如果隔了固定一段时间对端发现没有接受过本端请求,对端就会询问本端是否存在,如果不存在就会断开连接

这也叫TCP的保活机制

注意:保活机制一般与业务逻辑强相关,所以一般是应用层进行编写


总结:
可靠性部分

1.序列号

2.超时重传

3.确认应答

4.流量控制

5.拥塞控制

6.连接管理

性能部分:

1.滑动窗口

2.捎带应答

3.延迟应答

4.快重传

3.TCP与UDP对比

TCP是可靠传输,UDP是不可靠传输

TCP的报文丢失之后会进行重传,确保报文全都发送完成

UDP的报文丢失之后不做任何处理,直接丢弃

特点:

TCP可靠,不能保证实时性

UDP不可靠,但是实时性较高

相关推荐
EnCi Zheng4 小时前
L1D-Linux系统Node.js部署Claude Code完全指南 [特殊字符]
linux·运维·node.js
qq_172805594 小时前
wsl ubuntu层开放22端口,主机windows层开放2222端口转发
linux·windows·ubuntu·wsl
yy_xzz4 小时前
【Linux开发】 01 Linux TCP 网络编程——普通服务器
linux·网络·tcp/ip
阿kun要赚马内4 小时前
计算机网络:TCP三次握手
网络·tcp/ip·计算机网络
Trouvaille ~4 小时前
【项目篇】从零手写高并发服务器(九):HTTP协议支持——从TCP到应用层
linux·服务器·c++·tcp/ip·http·高并发·应用层
HalvmånEver4 小时前
Linux:基于 UDP Socket 的实战项目 --简单双向通信程序
linux·单片机·udp
落羽的落羽4 小时前
【Linux系统】中断机制、用户态与内核态、虚拟地址与页表的本质
java·linux·服务器·c++·人工智能·算法·机器学习
零K沁雪4 小时前
skb_buff 相关函数
linux·内核
!沧海@一粟!11 小时前
麒麟Zabbix Agent安装配置全攻略
linux·服务器·zabbix