传输层协议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正常四次挥手

机器断点/网线断开:

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

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

这种机制就是连接保活

相关推荐
minji...2 小时前
Linux 高级IO(三)多路转接之poll,poll的原理,poll版本的TCP服务器的实现
linux·服务器·网络·select·多路转接·epoll·poll
梦奇不是胖猫2 小时前
[ 计算机网络 | 第四章 ] 网络层 01 概述
网络·网络协议·计算机网络
小此方2 小时前
Re:Linux系统篇(二十四)进程篇·九:进程从创建到退出的底层机制与写时拷贝全解
linux·运维·驱动开发
艾莉丝努力练剑2 小时前
【Linux网络】Linux 网络编程:传输层TCP(二)
linux·运维·服务器·网络·tcp/ip·计算机网络
都在酒里2 小时前
Linux字符设备驱动开发(九):内核定时器——实现LED周期性闪烁与轮询驱动原理
linux·运维·驱动开发·交互
都在酒里3 小时前
Linux字符设备驱动开发(十):综合实例——I2C传感器 + LED智能控制与进阶指南
linux·运维·服务器·驱动开发·交互
2301_8090511410 小时前
Linux 网络编程 学习笔记
linux·网络·学习
坤昱10 小时前
cfs调度类深入解刨——最新内核细节分析2
linux·服务器·cfs·cfs调度·eevdf调度·eevdf·kernel 7.1
艾莉丝努力练剑11 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext