UDP和TCP的核心

UDP协议与TCP协议

UDP特点:无连接,不可靠传输,面向数据报,全双工

UDP/TCP/IP的报头都是二进制的,只有HTTP的是文本格式

UDP报文格式

报头部分

一个八个字节,每个部分占两个字节,端口号是属于传输层的

长度表示的是整个报文的长度,2个字节就是16个bit位,也就是能存0-65535个字节,65535/1024约等于64KB

一个报文就是64kb

一个端口号的取值是0-65535,但是一般我们只使用1024-65535

校验和

当传输一个很大的数据的时候,因为我们最多的传输64kb这个过程中钥匙收到外界的干扰,导致出现了比特翻转,那么就会导致数据发生改变

具体流程

Udp在发送之前,会把整个数据报带入计算一个校验和,然后把这个校验和与数据报整个发出去,对端收到之后就会重新计算一下校验和,如果发现不对,就会直接丢弃整个数据报,UDP的校验主要是CRC的方式,即把除了校验和之外的部分全部当做整数进行计算,拿到最后的结果会和对方发来的进行对比。

一般来说,只要原始的数据一样,使用相同的算法进行计算,那么校验值就应该一样

我们认为,只要校验和一样,就认为原始的数据是一样的(虽然可能会出来比特翻转,恰好两个翻转又抵消的情况,但是这个情况概率极低极低,可以忽略不算)

TCP协议

TCP的特点

可有连接,可靠传输,全双工,面向字节流

TCP的协议格式

从端口号开始到选项,都是属于报头,数据部分属于载荷

端口号和目的端口号

这里属于传输层的核心,表示来自哪个端口,要去到哪个端口

TCP报头的长度

这个表示的是整个TCP报头的长度4个bit位,可以表示0-15,这里你可能会有疑问报头部分不算上选项部分,单单是固定的长度就占20bit位了,这里怎么可能够,别急,这是因为,TCP这里非常聪明,使用4个字节为一个单位,也就是这里可以容纳下4*15=60个bit位。

选项

这个是TCP报头里面可有可无的东西,长度最多可以占据40个字节,一般存放的是MSS,窗口扩大因子之类的

保留

这个的存在是为了防止出现UDP的问题,长度不够,但是又无法扩展,所以为了解决这个问题,就现在报头占个位置,后续有啥情况就可以改动,单纯的预防作用

TCP的六个核心标志位

  1. 校验和

检查数据是否出错

TCP的十大核心机制

TCP的核心:可靠性

核心机制一,确认应答

保证可靠性的前提,就是需要知道自己发送的消息对方是否已经收到,在TCP中,收到对方的信息,我们需要给对方发送一个确认应答报文ACK,让对方知道我们已经收到信息了

举例子

我给女神发消息,问女生吃不吃,这个时候女神这边就需要给我回应,让我知道他有没有收到这个信息,这个返回就叫做ACK

后发先至问题

举例子

我先问吃不吃麻辣烫,再问做我女朋友好不好,但是由于网络传输过程中,可能每个传输走的路线不一样,导致后续的消息先到,先发的消息反而后到,这就会导致出现问题

TCP解决方式

TCP会对传输的数据,进行编号

针对我的每个问题进行标号,每个问题返回一个ACK,这就不会乱了,在TCP中,就是序号,针对每个序号都会有确认序号

当ACK为1时,表示这个是应答报文

序号和确认序号

在TCP中,因为TCP是面向字节流的,所以在编号的过程中,不是按照条数来编号的,而是按照字节来进行编号的,每个字节都有一个属于自己的编号,编号是连续递增的

一个TCP的载荷是多个字节构成的,意味着这里有多个编号,那么这个序号这里应该填什么?

填第一个字节的序号

确认序号填写什么呢?

把收到载荷的最后一个字节➕1,填写到确认序号中

举例子

主机A先把1-1000的数据发送给主机B,这个时候主机B会收到并返回一个TCP的头部报文确认序号哪里会写下一个是1001,告诉对方我已经收到1001之前的全部数据,这个编号是针对载荷的,报头并不会编号

在引入序号和确认序号,就解决了这个后发先至的问题,在TCP中,就是确保应用程序通过socket.api读到的数据顺序是正确的

在TCP中,接受方会有一个接受缓冲区,用来接受数据,在这个缓冲区中,会根据序号进行排序,这个时候小的在前,大的在后,只有确保前面的数据到了,read才会接触阻塞,如果后面的数据先到,就会一直等待

所以,在基于TCP进行写代码的时候,我们是不需要担心数据顺序的问题

TCP核心机制二,超时重传

因为TCP的核心机制是可靠性,所以针对可能出现的丢包情况,TCP也有防范的手段

为什么会丢包?

数据在经过路由器或交换机的时候,可能这个路由器或者交换机的已经超负荷工作了,无法再进行转发多一个了,就会导致出现丢包

举例子

现在A和B进行通讯,A给B传输数据,B需要返回一个ACK

丢包的两种情况

  1. A发的数据丢了
  2. B返回的ACK丢了
第一种

由于A在发的过程中丢包了,但是他不知道自己丢了,所以在等待到一定的时候后,就会触发超时重传,再给B传输这个数据。

第二种

A传输个给B成功了,但是B返回的ACK丢了,这个时候,A不知道是发失败了还是ACK丢了,他能做的只有重新发送数据,知道B的ACK到达A。

这个过程中B收到了两份一样的数据,会不会导致后续读到两份一样的数据?

不会,因为TCP有接收缓冲区,在这里会对接受到的序号进行排序,如果发现一样的就会丢弃,不一样的才会保存

在上述过程中,A不知道到底是出现了哪种情况,所以就只能不断的重传,

假设A和B发送数据,丢包的时间阈值为T,如果A和B传输过程中,超过了这个阈值的时候,就会延长这个时间阈值,但是并不会是无止境的延迟,当到达一定程度的时候,就会放弃这次传输,如果传输很多词都不成功,那就不传了,说明网络产生了重大的故障。

TCP协议最核心的两个机制:确认应答和超时重传(确保了TCP能搞进行可靠的传输)

TCP核心机制三,三次握手和四次挥手

因为TCP的特性是有连接

三次握手

建立连接的过程我们就称之为三次握手,这三次握手是不带业务的,只是打招呼,和生活中认识一个新的朋友一样,一开始是握手只是传递信号,打招呼

Synchronize:同步的意思,当SYN为1的时候,表示这个是同步报文

此时都不携带任何的业务内容,单纯的只是打招呼

三次握手

A会给B发一个请求,告诉B,接下里我要和你建立连接,你把我的关键信息保存好,同时也把你的信息发送给我

B就会返回一个ACK应答报文同时把自己的关键信息也发送给A

这个时候A再返回一个ACK表示收到

为什么有时候会称作是四次握手

因为四次握手,就是把B返回的ACK和B的syn分为两步走,没有合并,这样做的坏处是会消耗更多的网络带宽,三次握手就把他们合并起来,提高传输的效率

三次握手详细过程图

Closeed是不存在的状态,还没有创建tcp连接,方便理解所以就存在

LISTEN :当服务器启动的时候,new ServerSocket的时候,就会进去listen状态

SYN-SENT:客户端发起握手,请求建立连接

SYN-RCVD:服务器收到并返回ACK和SYN给客户端

ESTABLISHED:就绪状态,表示连接成功,可以开始进行通讯

注意SYN-SENT和SYN-RCVD的存在时间极短,肉眼基本上看不到,如果出现了并长时间停留,就要考虑一下是不是代码出现了问题

举例子

就相当于你手机充满电,信号良好,随时可以打电话,就绪状态就相当于已经打通,对方也接了,说话就行了

TCP三次握手的意义是什么?
  • 投石问路

相当于先初步的探测一下这个网络的通信链路是否流畅

  • 确认双方的接受能力和发送能力

A先给B发送信息,如果B收到了,就说明A的发送能力和B的接受能力没问题

B给A发送消息,如果A收到了,就说明B的发送能力和A的接受能力没问题

然后A要告诉B你的能力都没问题

  • 协商一些关键信息

建立好连接之后,就会进行协商关键信息,比如传输的初始序号是多少

举例子

A给B传输消息的时候,约定好使用8080端口,初始序号是1000,如果这个时候其中有个数据包迟迟没有传输到B,甚至等到这次连接结束都没到,那么下次建立连接,约定好使用8080端口,初始序号是2000,这个时候之前迷路的数据包到啦,那咋办,丢弃掉,因为初始序号都不一样,指定是叛徒,丢了就行

总结

TCP三次握手的意义

  1. 投石问路,确定网络链路是否通畅
  2. 验证通讯双方的接收和发送能力是否正常
  3. 协商关键的信息

四次挥手

示意图

通讯的双方,会给对方发FIN告诉对方,我要删除你的关键信息,结束这次通讯

A给B发FIN

B会给A发一个ACK代表收到

B还会个A发一个FIN告诉A我也要删了你

A也会回复一个ACK

四次挥手对应的是六个关键中的FIN

四次握手能够合并成三次,那四次挥手能合并成三次吗

可以但是又不可以

可以

延时应答,通过程序员写代码的干预,可以让他们一起返回

不可以

因为B的ACK是内核负责返回的,而FIN是由程序调用close才会关闭,他们两个的交互时机是不一样的,所以不能合并

四次挥手无论是客户端还是服务端,都是可以先行提出的,但是三次握手就不行,必须要客户端先提出

四次挥手具体实现

谁发起FIN,谁就会进入TIME-WAIT状态

谁是被动发起FIN的一方,谁就会进入CLOSE-WAIT状态

TIME-WAIT状态的是为了防止最后一个ACK丢包,进行托底的作用

举例子

客户端发起FIN(结束进程/调用close),服务器就会进入CLOSE_WAIT并感知到然后由内核返回ACK,这个状态是很快完成的,然后会返回一个已经close给对方,对方这个时候就会进入TIME-WAIT状态并返回一个ACK

在这段代码中,我们可以看到,当服务器读取到客户端发起FIN的时候,内核会返回一个ACK至于FIN会不会跟着返回,就要看try这个代码是怎么写的。

如果发现这个Close-WAIT持续很长的时间,那估计就是代码出现了bug,没有执行到close

TIME-WAIT

TIME-WAIT存在的意义就是为了解决最后这个ACK可能丢包的问题

假设现在A和B进行四次挥手

A发起FIN给B

B接收到由内核返回ACK

B调用close方法并返回FIN

A接收到并返回ACK

这个过程中,如果这个ACK发出去后,A就马上释放连接,那加入ACK丢了,那B就遭老罪了,B会以为A没有收到这个FIN,会进行超时重传,虽然说最后也是会释放掉这个连接,但是期间多很多没有必要的资源消耗,所以就出现了TIME-WAIT,在发送ACK之后等会,等一段时间对方没有再发FIN的情况下,再释放连接

那等多久合适?

一般来说TIME-WAIT等待时间为2*MSL(任何两个节点传输之间需要的最大时间)

总结

LISTEN:启动服务器等待连接

ESTABLISHED:已经连接好了,可以进行通讯

TIME-WAIT:为最后一个ACK丢包做预防

CLOSE-WAIT:存在时间极短,对方发来FIN之后就会调用close

TCP核心特性四,滑动窗口

TCP的核心是可靠性,在保证可靠性的同时,就付出了效率的代价,那如何提高效率,就引入了滑动窗口

单个传输

原先的传输是一个个进行传输的,这样的效率就很低

批量传输

把数据包合成一大组,然后一次性发过去,只需要花一份等待ACK的时间,就可以获得多组ACK

下一组怎么发?

等上一组全部返回再发吗?并不是,只要上一组返回其中一个,我就接着发,这样就提高了效率,如果等上一组全部返回再发,效率太低了

滑动窗口

高亮度的区域表示等待ACK的区域,当1001到达对方支付,就往后移一个,在告诉运动的情况下,就好像是滑动窗口

如果是2001先返回怎么办?

滑动两个窗口,为什么?

因为确认序号是2001的情况下,代表着2001之前的数据全部都收到了

窗口越大,批量发送的数据也就越多,效率也就越高

窗口过大,就会导致可靠性降低

滑动窗口丢包情况分析

  1. 数据包到达B,ACK丢了
  2. 数据包没到达B,直接丢了
情况一,ACK丢了

这个情况下的丢包是无所谓的,因为后一个ACK可以包含前一个ACK的含义

假设现在我们数据包全部发到B了,这个时候的B返回的ACK丢了1001和2001,甚至全部都丢了,只剩下6001返回了,没有任何影响,因为返回的确定序号6001代表着6001之前的数据全部都收到了,所以丢了就丢了,无所谓的

情况二,数据包丢了

A在给B传输数据包的过程中,假设丢了1001这个包,那么返回的确认序号就会提示1001未收到,无论你后续发的数据包是否丢包,都会先解决这个问题,当连续三次发送同一个确认序号的时候,A就会意识到丢包了,就会重新发送这个数据包,之前发送的数据包如果没丢包,那么B就会返回没丢包的下一个确定序号

举例

解决问题先解决问题,擒贼先擒王,解决完主要的其他就好了

快速重传

丢了谁就传谁,其他收到的就不用重新传输了,过程很快,这个属于是在滑动窗口下,超时重传的变种操作

快速重传vs超时重传

两者并不矛盾

超时重传代表传输的数据少

快速重传是适合在数据量多的情况

举例

类似于牛顿的力学相对论和相对论

TCP核心特性五,流量控制

如果滑动窗口太大,就会影响到可靠性,因为接收方处理的能力是有限的

是根据接受方的处理能力进行限制的

举例子

现在要进行一边蓄水一边放水,如果蓄水的速度太快,放水的速度又赶不上,那么就会出现一个问题,水会溢出来,就会出现丢包的情况

流量控制,就相当于踩一下刹车,让对方的速度慢一些,处理不过来了,,根据接收方的处理速度反馈给发送方,限制发送方的传输速度

在ACK中,依赖窗口大小进行表达

这里会表示滑动窗口的大小,这个是动态变化的,表示的是现在最大能接受的数据是多少

那这个最大是64kb吗,和UDP的一样?

并不是,这个窗口的最大数值确实是64kb但是在选项里,有一个特殊的属性叫窗口扩展因子,可以把这个窗口进行指数被的扩展,让窗口变得很大

具体流程

首先,A发送数据的时候,B会在返回ACK的时候,返回一个窗口值,这个是B目前能接受的最多数据值,那A就会知道,不能超过这个,否则就会数据丢包,所以发送的数据就会比这个小,如果当B返回ACK的时候告诉A处理不过来了,那么A就会停止发送数据,发送一个窗口探测,这个窗口探测包,内部不包含任何的数据,只是单纯的为了获得还能发多少,类似于问B咋样了,还能再继续不,否则等B的数据消耗完了,A还愣着不发数据

缓存和缓冲区的区别

缓存(cache)把之前读过的数据放到一个更近的地方,让下次拿的时候更方便

缓冲区(buffer)把低效的次数变少,多次的抵消操作合并成一次

TCP核心机制六,拥塞控制

拥塞控制是针对数据传输链路的能力进行控制的

举例子

揉面团

面多加水,水多加面

示意图

在传输的过程中,要经过很多的路由器和交换机,在这个过程中,,一次能传输多少不取决于能承载最多的设备,取决于最少的设备(木桶原理)

启动的时候,会先以小的窗口运行,如果很不丢包,就会加大窗口,入股丢包了,就继续降低速度,反复进行,动态平衡

拥塞机制和流量控制都能限制发送方的发送速度,谁小就取决于谁

示例图

一开始会先以小窗口进行传输,然后指数被增长,到了一定的范围之后,就会变成线性增长,如果发现丢包(收到3个一样的ACK),就会再减小,达成动态平衡

和谈恋爱一样,热恋期,平淡期,吵架之后的恢复期

TCP的核心机制七,延时应答

默认情况下,一般我们收到数据包,第一时间就会返回ACK,但是现在我们可以通过延时应答来返回ACK从而提高效率

举例子

原先我们立即返回,那么就会返回200,如果我们延时返回,等程序消耗掉一部分数据再发送ACK,那么就看会返回300,尽可能的提高了窗口的大小,就是这个时候又收到了发送发的数据,也不会因为我们没有返回ACK就罢工,因为我们后一个ACK是可以包含前一个ACK的信息的

所有的包都可以延时应答吗?

不行

数量限制和时间限制

总不可能你每个都不回,这样会导致触发超时重传,跟耗费资源

TCP核心特性八,捎带应答

基于TCP有延时应答,引入捎带应答,在返回数据的时候,把上次要返回的ACK也带回去,可以提高效率

如果没有延时应答的话,那么返回ACK的时间和返回响应的时机是不同的

引入延时应答,此时如果有数据要返回,就可以顺手带一下ACK一起返回

典型例子:三次握手

把两个包合成一个,只花费一份网络带宽(懒汉模型)

TCP的核心机制九,面向字节流

粘包问题

粘的是应用层的数据包,因为TCP是面向字节流的,所以传输的过程是以字节传输的,很容易就混淆了包和包之间的边界导致无法区分哪里到哪里是一个完整的数据包

示例

假设我现在传入aaa,bbb,ccc

那么对于接收方read的时候,就会出现问题,哪里到哪里是一个包aaabbbccc是一个包,还是aaab?还是aaabbbc?

这个问题,对于TCP来说,是无法解决的

所以我们需要在应用层就解决

1、使用标识符

约定包与包之间的分隔符

使用/n标志包的结束

这样我传入aaa/n bbb/n ccc/n的时候,接收方收到我的数据,就知道aaa是一个包,bbb是一个包,ccc是一个包

2,约定长度

约定每个包的开头四个字节是表示长度,表示这个包有多长

3aaa 4bbbb 5ccccc

这样当接收方接受数据的时候,就可以知道

aaa是一个包 bbbb是一个包 ccccc是一个包

在HTTP中两种方案都有体现

  1. GET请求,使用空行表示结束
  2. POST请求,有body会使用Content-length表示body的长度

TCP的核心协议十,异常情况的特殊处理

TCP通信过程中存在的特殊情况

进程崩溃

虽然进程结束了,但是由于TCP是在操作系统内核的,所以TCP的释放时机是更晚的,即使进程结束了,TCP的连接信息还存在,所以四次挥手会继续进行

主机关机

正常关机的情况下,如果关机的时间足够,那么就会正常进行四次挥手如果时间不够最后也是会释放的

假设现在A发起了挥手,B收到之后返回ACK,然后A就关机了,这个时候B的FIN还没传输到,但是A已经关机了,这个时候B因为没有收到ACK,就会进行超时重传,如果进行多次之后没有反应,就会认为A存在故障,就会主动释放连接。

主机断电
接收方掉电

假设接收方掉电

A突然掉电,对于B来说,后续的数据都没有ACK,那么B就会触发超时重传,如果重传达到一定的次数,就会触发重置TCP连接,意思是前面的全部不算,重新再来,主动发送一个复位报文,如果还是没有ACK那么就会释放这个连接

RST(复位报文)

发送发掉电

如果是发送方掉电了,B无法区分A是挂了还是休息,所以B会在等待一段时间之后,给A传输一个特殊的报文-心跳包,里面不携带任何的业务数据,单纯的只是为了触发ACK和前面的探测包类似,如果对方返回了ACK说明对方还没挂,就会继续等待,如果对方没有心跳了就会触发RST尝试,如果还是失败就会单方面释放掉连接

心跳包(保活机制)

在分布式的系统中,心跳包使用的非常广泛,TCP虽然内置了心跳包,但是等待时间很长很长,所以现在一般会自己实现在应用层

网络断开

所有的情况,和ab掉电的情况是一样的

URG/PSH

URG:紧急指针位

理解为插队,正常情况下是按照序号发送的接受,使用URG就相当于有紧急情况需要插队

PSH:催促标志位

发送方发送的时候如果带有这个标志,代表着这个数据需要优先处理,对方就收到之后,就会先把这个数据进行处理返回

TCP与UDP

TCP的核心就是可靠传输,在大部分场景下,都是会使用TCP

UDP的核心就是速度快效率高,在不怕丢包,追求效率的情况下可以使用(机房)

如何基于UDP实现可靠传输(面试)

  1. 使用应答机制
  2. 使用超时重传

补足了数据传输的可靠性

  1. 使用TCP的握手和挥手机制

管理生命周期

  1. 滑动窗口
  2. 流量控制
  3. 拥塞控制

提升效率

  1. 延时应答
  2. 捎带应答

减少网络开销

总结

TCP的十大特性

  1. 确保数据传输的可靠性

应答机制,超时重传

  1. 提升传输效率

滑动窗口,流量控制(根据接收方的处理速度),阻塞控制(根据传播路径中承受力最弱的设备)

  1. 减少网络开销

延时应答和捎带应答

  1. 管理生命周期

TCP的握手和挥手机制

  1. 解决粘包和异常问题

粘包问题是通过应用层约定结束来解决

异常通过超时重传和心跳包解决

相关推荐
leo__5202 小时前
MATLAB 实现 基分类器为决策树的 AdaBoost
开发语言·决策树·matlab
Alsn862 小时前
27.IDEA 专业版创建与打包 Java 命令行程序
java·ide·intellij-idea
老朱佩琪!2 小时前
Unity原型模式
开发语言·经验分享·unity·设计模式·原型模式
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于JAVA的车辆违章信息管理系统设计及实现为例,包含答辩的问题和答案
java·开发语言
麒qiqi2 小时前
【Linux 进程间通信】信号通信与共享内存核心解析
java·linux·算法
后端小张2 小时前
【Java 进阶】深入理解Redis:从基础应用到进阶实践全解析
java·开发语言·数据库·spring boot·redis·spring·缓存
柯南二号2 小时前
【后端】【Java】RabbitMQ / RocketMQ / Kafka / Redis 消息队列深度对比与选型指南
java·java-rocketmq·java-rabbitmq
木心爱编程2 小时前
【Qt 5.14.2 新手实战】QTC++入门筑基——10 分钟做个文本编辑器:QLineEdit + QTextEdit 核心用法
java·c++·qt
楠枬2 小时前
Nacos
java·spring·spring cloud·微服务