传输层协议UDP和TCP

UDP主要负责将数据从发送端传输至接收端

端口号(port)标识了一个主机上进行通信的不同的应用程序

端口号划分:

0-1023:知名端口号,与http,ftp,ssh登应用层协议强绑定,这些端口号都是固定的

1024-65535:操作系统动态纷纷的端口号,客户端程序的端口号,就是有操作系统从这个范围划分的

一个进程可以bind多个端口号

但是一个端口号不能bind多个进程

UDP协议

协议本质上就是struct

带上报头,就是把结构体和数据拷贝合并起来,就是封装

UDP分为16位源端口号,16位目的端口号

16位UDP长度,16位UDP检验和

总共8字节

UDP如何解包:直接读取UDP报文的前八个字节

UDP如何分用:根据16位目的端口号在系统中查找端口号

16位UDP校验和,查看报文是否完整,不完整就丢弃

udphdr就是UDP的报头

sk_buff是一个报文的管理结构

报文由sk_buff组成的双链表进行管理

sk_buff中存在报文的信息(数据)

sk_buff中也存在head和end,指向报文的开始和结束

报文到达传输层,需要给报文添加UDP协议

step1:head指针向前移动,给UDP协议提供空间

step2:填充UDP报头部分

UDP的特点:

无连接:直到对端的IP和端口号就直接进行传输,不需要连接

不可靠:没有确认机制,没有重传机制,如果因为网络故障,该段无法发到对方,UDP协议也不会返回错误信息

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

UDP没有真正的发送缓冲区,而是调用sendio直接交给内核,将内核的数据传给网络层协议,进行后续的传输动作

UDP有接收缓冲区,但是接收缓冲区不能保证UDP报的顺序和发送UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃

UDP传输数据的最大长度是64K

TCP协议

TCP协议全称是传输控制协议

对数据的传输进行一个详细的控制

用户将数据拷贝给了OS(传输层)

后续数据的发送:什么时候发?发多少?出错了怎么办?

全都有OS管理

TCP报头格式

TCP标准报头长度是20字节,是固定长度的标准报头

16位源端口号,16位目的端口号,分别是来源和目的地

32位序号,32位确认需要序号

报文再网络中传输,是要花费时间的,发送报文时,发送方不知道接收方有没有收到

接受方在收到报文后,会给发送发发送一个应答,这样,发送方(客户端)就可以确认接收方(服务器)是否收到了报文

这种行为就是确认应答

发送方收到了确认应答,就保证了发送报文的可靠性

长距离通信时,没有100%的可靠性,因为总有最新的消息是没有应答的

request = tcp报头 + 有效载荷

发送报文的过程是串行的,发消息需要接收到应答,才能发下一个

为了提高效率,系统一次性发送多个报文,应答时,也会所有报文一起应答

系统会给tcp报文带上编号,来完成对报文的确定,保证报文和应答一一对应

这些编号,就是32位序号的意义

确认序号 = 序号 + 1,表示序号之前的内容已经全部收到

报文如果是乱序的,也是不可靠的表现

序号的意义,就是保证报文的按序到达

这种发送方式,才是TCP真实的发送方式

当发送应答时,同时附带消息,就是捎带应答

此时TCP报文,既是应答,又是数据

此时会使用序号和确认序号,因此需要存在序号和确认序号两种序号

客户端给服务器发消息时,如果对方来不及接受

一般会直接将接受不了的报文丢弃,但是这样太浪费时间和空间了

因此tcp为了保证可靠性,客户端会根据S端的接受能力来进行流量控制

由接收缓冲区的剩余空间大小来决定

16位窗口大小就是用来表示对方接收缓冲区剩余空间的大小

4位首部长度,保留6位

tcp如何解包?

由于TCP是20字节固定长度的标准报头

所以直接解包20字节

20字节中一定可以获取4位首部长度

4位首部长度,是有基本的计算单位的:4字节

报头长度 = 4位首部长度 * 4

因此4位首部长度可以表示标准报头的长度

报头字段至少20字节,加上选项就是报头长度,4位首部长度实际表示20, 60

16位窗口大小

就是用来表示对方接收缓冲区剩余空间的大小

通信双方的报文,都是发给对方的,所以16位窗口大小,会填写自己的接收缓冲区剩余空间的大小

6位标志位

报文存在很多种类

如建立连接的请求,正常的数据通信,断开连接的请求

服务器需要能够区分不同报文的类型

标志位:

ACK:表明自己是一个确认报文,说明报文含有确认功能,由于存在捎带应答,所以大部分tcp报文,ACK都是1

SYN:同步标志位,建立连接的请求

FIN:连接断开标志位,通信结束时,进行握手协商

16位检验和,16位紧急指针

选项

数据

tcp报头里没有描述数据的长度,因为tcp面向字节流,收到tcp报文,去掉报头后,剩下的数据直接放入缓冲区,不做任何解释

tcp的发送和接收缓冲区本质就是一个数组

在将数据放入缓冲区后,数组中每一个字节的数据,天然就有了编号

这个编号就是32位序号的来源

发送就是将数组里的内容拷贝到另一个数组中,所以数据是字节流就是字节流

丢包

1.数据直接丢失

主机A在发送报文一段时间后,如果没有收到应答,就重新发送报文

这就是超时重传机制

2.应答丢失

主机A不能确定是自己的数据丢失,还是应答丢失

因此主机A不管是哪种丢失,都没有收到应答

因此也是超时重传机制

如果应答丢失,主机A依旧重传,此时主机B就收到了两个相同的报文

这是TCP就会根据报头中的序号来对相同的报文去重

超时时间是动态计算的

以500ms为单位进行控制

每次判定超时时间都是500ms的2^n倍

三次握手和四次挥手

tcp是要进行链接管理的

tcp建立链接,需要进行三次握手,

断开链接,需要四次挥手

connect用来触发三次握手

accept不参与三次握手,只是等待三次握手自动完成

三次握手是由双方操作系统自动完成的

只要发出SYN,就会进入SYN_SEND

只要收到SYN,就会进入SYN_RCVD

只要把ACK发出,就会进入ESTABUSHED,即连接已经建立

但是无法保证ACK一定被收到

因此三次握手,可能会失败,但是只要三次握手成功,连接就一定可靠

三次握手的本质就是四次握手

但是ACK和SYN进行了捎带应答

建立连接要征得双方同意,因为TCP是全双工的,需要建立两个朝向的连接

用户通过fd,可以实现read,write

三次握手可以验证通信的信道是通畅的

三次握手完成后,双方就建立好了连接

一条连接,一定和一个文件对应,因为一个连接一个fd

连接在OS内部会存在很多个

OS要对连接进行管理,维护连接时需要成本的(时间+空间)

因此连接建立多了,就会产生很多消耗

断开连接需要征得双方同意,因为TCP是全双工的,要关闭两个朝向的连接

四次挥手就是互相发断开请求和接受断开请求

在进行通信时,调用close(fd)触发四次挥手

一个close(fd)一次FIN,ACK,close是关闭双方的通信

如果服务器故意不关闭sockfd,只会完成两次挥手

服务器会长时间处于close_wait状态,剩下的两次握手就不会完成

所以一旦使用完毕sockfd,就要关闭不用的sockfd

关掉服务器,就会自动close,但是此时客户端已经不会回应了,一段时间后,服务器会自动CLOSED

主动断开连接的一端,会在发送完毕最后一次握手后,等待一段时间,处于TIME_WAIT状态

等待两个MSI(一个报文在网络中存在的最长时间)的时间才能回到CLOSED状态

因为主动连接的一方,需要等待历史的游离报文在网络中消散(可能存在一些被客户端判定超时的报文,实际上没有丢失,还是到达了主机,需要等待他们消失)

同时也要尽可能的正常进行4次回收,完成连接断开(因为最后一个ACK可能丢失)

TIME_WAIT时的端口号,默认不能使用,但是此时的端口号时空闲的

如果想要使用这个端口号,需要手动将这个端口号设置为可复用

ACK是自动ACK的

使用shutdown可以单方面关闭通信

此时外卖不再能给对方发送消息,但仍能收到对方的消息

即只关闭write,不关闭read

选择close还是shutdown由OS决定

标志位:

ACK:表明自己是一个确认报文,说明报文含有确认功能,由于存在捎带应答,所以大部分tcp报文,ACK都是1

SYN:同步标志位,建立连接的请求

FIN:连接断开标志位,通信结束时,进行握手协商

流量控制

接收端处理数据的速度有限,如果发送端发的太快,导致接收端的缓冲区被打满,多余的报文就会丢包,然后导致丢包重传等连锁反应

因此需要流量控制

16位窗口字段,存放了窗口大小信息,但是TCP窗口大小不止65535字节

TCP首部40字节选项中还存放了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位

流量控制 != 发送变慢

双方在发送数据前进行过三次握手,因此直到彼此的最大接受能力

滑动窗口

TCP实际发送数据是并行发送许多数据,然后统一应答

但是由于有流量控制,所以不能一次性发送太多数据

为了支持TCP并行发送数据,但是又不能一次发送太多数据

在发送缓冲区限定了一块区域,将发送缓冲区分为了3部分

在区域内的数据是可以直接发送的,暂时可以不用应答

前面的是已经发送并确认的,后面的是待发送的区域

这个可以直接发送的区域,就是滑动窗口

start = 确认序号,end = start + win

流量控制是通过滑动窗口实现的

滑动窗口可以变大,变小,甚至为0

当缓冲区的数据被取走后,缓冲区变大,滑动窗口就自动变大了,其他情况同理

超过发送缓冲区,不会导致越界,因为缓冲区是一个环形结构

滑动窗口异常丢包

由于确认序号表示:改序号之前的报文已经全部被收到了

1.最左侧丢失

最左侧丢失,滑动窗口不会右移,确认序号依旧是1

此时客户端收到的应答的确认序号都是1

所以可以确认至少1-1000丢失了

此时主机A会进行补发,补发完后,收到正确的应答后,确认序号直接到达4001

因此不担心窗口会跳过最左侧的问题

2.中间丢失

应答的确认序号是丢失报文前的报文的序号

此时滑动窗口就能向右滑动

随后转换成最左侧丢失的问题

3.最右侧丢失

最右侧丢失也是滑动窗口右移,转化成最左侧丢失问题

在发送方连续收到3次相同的确认应答后,会立刻对对视的报文进行重传

这个机制被称为快重传(高速重发控制)

PSH

如果缓冲区满了,会持续进行窗口探测

如果窗口探测持续为0

PSH会被置为1,告诉对方,请尽快将缓冲区的数据交给上层

PSH实际的意思是:数据较为重要,请尽快交付

RST

RST = reset

如果三次握手失败(ACK丢失),此时客户端认为连接建立好了,服务器认为连接没建立好

此时客户端会直接向服务器发送数据,这是服务端会像客户端应答,并将RST置为1

之后客户端会将连接重置,再重新三次握手

URG

由于TCP的报文自带序号,所以无法做到让新到的报文插队

但是可能存在需要插队的情况,这是就需要使用URG

如当我们需要终止一个大型文件的传输时,可以将URG置为1,标识该报文为紧急报文

此时OS会优先将该报文的有效数据提取出去,让上层优先读到

16位紧急指针其实是一个偏移量,会标识紧急数据在有效载荷的什么位置

紧急数据只占一个字节

拥塞控制

如果发送数据时,大量的报文丢失,发送端会判断网络出现拥塞问题

此时不能立即重传,否则会由于网络拥堵,所有该网络下的主机都会进行超时重传

所有的主机几乎在同意时间进行大量数据的重传

会导致网络的进一步拥堵

如果识别到了网络拥堵

就会使用慢启动机制来解决

先发送少量的数据探路,摸清当前网络的拥堵状态,在决定按照多大的速度传输数据

拥塞窗口默认为1

没收到一个ACK,拥塞窗口+1,慢启动下的拥塞窗口大小实际上是指数增长

滑动窗口 = min(对端接收缓冲区剩余空间大小, 拥塞窗口)

由于指数增长过快,我们引入了一个慢启动的阈值

当拥塞窗口超过阈值,不再按照指数方式增长,而是按照线性方式增长

一旦再次拥塞,就会重新慢启动,新的慢启动阈值 = 上次拥塞窗口大小 / 2(乘法减小)

拥塞控制算法:慢启动 + 假发增大 + 乘法减少

拥塞窗口本质是网络健康情况的评估值

网络的拥堵健康等状态,一定是变化的,所以拥塞窗口会一直变化,不断评估情况

延迟应答

用户发送报文给另一个用户

如果收到报文的用户正在取数据,则该用户会稍等一会再应答

此时上层可能会把数据读取走,接收缓冲区剩余空间就会变大

则发送方的滑动窗口就可能变大

这就是延迟应答

延迟应答每隔n个包就会应答一次,通常n = 2,超时时间取200ms

捎带应答

捎带应答是双方在发送确认等信息时,附带上正常的需要发送的数据

三次握手的前两次不能携带数据

双方交换了win窗口,将各自的起始序号交给了对方

双方的序号支持随机化,这样可以减少网络上的游离报文影响正常数据

但是三次握手的第三次可以携带数据,就是捎带应答

应用层的报文完整性又用户层自己决定

粘包问题

粘包问题:用户层基于字节流读书应用层报文时,没有读到完整的报文

UDP没有粘包问题

但是TCP又粘包问题,因为TCP读取的报文可能不完整

为了解决粘包问题,我们在应用层,需要有明确报文边界的方式

1.报文长度固定

2.特殊符号作为分割符

3.固定长度的报头+报头属性中添加子描述长度字段

TCP异常

进程终止:

双方OS,正常进行四次挥手

机器重启:

机器重启之前,要关闭所有启动的进程,为了关闭进程,会有双方OS正常四次挥手

机器断点/网线断开:

服务器端(接收端)会认为连接正常,但是一旦接收端发现客户端(发送端)

服务器端会向客户端发送确认消息,如果客户端没有响应,服务器端就会关闭连接

这种机制就是连接保活

相关推荐
A小辣椒13 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒17 小时前
TShark:基础知识
linux
AlfredZhao19 小时前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao1 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
网络研究院2 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展