网络原理-传输层
一、UDP协议
UDP 数据报 = 报头 + 载荷
UDP报头一共有4个字段,每个字段两个字节(源端口,目的端口,数据报长度,校验和)
用2个字节表示端号,其取值范围为0~65535
1-1024都是系统自用的端口(知名端口号)
一个 udp 数据报的最大长度是64KB
校验和/检验和判断数据在传输过程中是否正确
使CRC算法来完成校验
UDP数据报发送方在发送之前先计算一遍CRC,再把算好的CRC放到UDP数据报中
接收端接收到该数据后,也会按照相同的算法,再算一遍CRC,比较自己计算的值和数据报中的是否一致,若不一致则发生了比特翻转
精度更高的校验积md5 /sha1
md5算法背后有一系列数学公式来计算
特点
1、定长:无论原始数据多长,算出的md5最终值都是固定长度
2、分散:计算md5的过程中,原始数据中只要变化一点点,算出的md5值就会差异很大
这样的特性使 md5 可作为一个 hash 算法
3、不可逆:源字符串计算md5值该过程简单,但给你一个md5值让他还原成原始字符串这是不可能的
据此 md5 也可适用于一些加密的场景
二、TCP协议
1、TCP报头:
源端口,目的端口和udp一样
首部长度(header):不像 udp 的报头固定是8个字节,TCP报头中前20个字节是固定长度
选项:TCP在没有选项时,TCP数据报长度默认是20字节,在有选项时,TCP数据报长度可以变长,最多为60字节
保留位:tcp设定报头的时候,提前预留了保留位,后面一旦用了再把这些保留位使用起来
6位标志位:tcp核心部分
16位校验和:把报头和数据载荷放到一起计算校验和
2、可靠传输
1、确认应答(用来确保可靠性,最核心的机制)
发送方发送了一个消息,接收方收到了会返回一个应答消息,看到应答就知道消息被收到了
网络传输中会出现先发后至的情况,引入序号和确认序号对数据进行编号
TCP是面向字节流的,以字节为单位进行传输,TCP的序号和确认序号都是以字节来进行编写的
由于序号是连续的,仅需要在报头中保存第一个字节的序号后续序号容易计算
应答报文中的确认序号是按照发送过去的最后一个字节再加一来设定的
例如A向B发送了1 - 1000 的字节数据,B收到后反馈应答报文,应答报文的确认值就是1001
1001的含义是:
1、1001以前的数据我已全部收到
2、接下来你该给我发从1001开始的数据了.
应答报文也叫ACK报文,平时六位标志位中该个位为0,若是应答报文,ACK值就为1
2、超时重传
网络上可能存在丢包的情况,若数据包丢了,没有到达对方,也就没有ack报文了,这时就要进行超时重传
TCP 的可靠性就定在对抗丢包,期望在丢包客观存在下,也尽可能把包传过去
发送方发送了一个数据后要等,等的时间内收到了ack就发送下一个,若等到了超时时间还没等到,此时发送方就认为数据传输出现丢包了,就会再传一个刚才的数据
上面的过程中,是认为没收到 ack 就是丢包,其实这样的结论有点小问题
丢包,不一定是发的数据丢了,还可能是 ack 丢了
数据丢了,还是 ack 丢了,发送方角度看起来,就是区分不了,都是 ack 没收到
若只是ack丢了,就需要去重了
TCP socket 在内核中存在接收缓冲区(一块内存空间),发送方发来的数据,是要先放到接收缓冲区中的
然后应用程序调用 read/scanner.next 才能读到数据,这里的读操作其实是读接收缓冲区
接收缓冲
当数据到达接收缓冲区的时候,接收方首先会先判定一下看当前缓冲区中是否已经有这个数据了(或者这个数据曾经在接收缓冲区中存在过)
如果已经存在或者存在过,就直接把重复发来的数据就丢弃了,就能确保应用程序,调用 read/scanner.next 的时候,不会出现重复数据了
接收方如何判定这个数据是否是"重复数据",核心判定依据:数据的序号
1、数据还在接受缓冲区里,还没被 read 走,此时,就拿着新收到的数据的序号,和缓冲区中的所有数据的序号对一下,看看有没有一样的,有一样的就是重复了,就可以把新收到的数据丢弃了
2、数据在接受缓冲区中,已经被应用程序给 read 走了,此时新来的数据序号直接无法再接受缓冲区查到
注意, 应用程序读取数据的时候,是按照序号的先后顺序连续读取的,先读 1-1000 1001-2000 2001-3000
一定是先读序号小的数据后读序号大的数据的(可以把接收缓冲区这个队列想象成带有优先级的阻塞队列)
此时 socket api 中就可以记录上次读的最后一个字节的序号是多少
比如上次读的最后一个字节的序号是 3000 新收到一个数据包的序号是 1001
这个 1001 一定是之前已经读过的了,这个时候同样可以把这个新的数据包判定为"重复的包"直接丢弃
超时会重传,但也不是无限重传,重传也是有一定策略的
1、重传次数是上限的,重传到一定程度仍无ack包就尝试重置连接,若重置连接失败就直接放弃连接
2、重传的超时时间阈值也是不固定的,随重传的次数增加而增加(重传频率越来越低)
3、连接管理
1、建立连接:三次握手
客户端向服务器请求连接的时候,会先发一包连接请求数据(SYN包)服务端同意就会回复SYN包+ACK包,客户端收到就回复一包ACK包,连接建立
2、断开连接:四次挥手(客户端或服务器都可以关闭连接)
客户站主动关闭连接需要向服务器发送一包FIN包,自己进入终止等待状态1,服务器收到FIN包后发送ACK包表示自己进入关闭等待状态,客户端进入终止等待状态2,服务器可以发送未发送的数据,客户端还可以接收数据,服务器发送完数据后发送一包FIN包进入最后确认状态,客户收到后回复ACK包进入超时等待状态,超时时间后关闭连接,服务器收到ACK包后立即关闭连接
3、握手的意义
1、三次握手先针对通信路径进行投石问路,初步的确认通信链路是否畅通(可靠的前提条件)
但是关键的可靠传输三次握手作用不大
三次握手仅在建立连接时起作用后续数据传输依赖确认应答
2、三次握手也在确认通信双方的发送能力和接收能力是否正常
3、三次握手的过程中会协商一些必要的参数
时接本质上是通信双方保存对方的信息,断开连接本质上就是把对端信息从数据结构中删除
相似之处:都是通信双方各自给对方发起一个syn/fin,各自给对方返回一个ack
不同之处:三次握手中间两次一定能合并,四次挥手则不一定
三次握手必须是客户端主动,四次挥手两者都可以主动
4、TCP转换状态
LISTEN状态:表示服务器这边创建好serversocket了,并且端口号绑定完成
ESTABLISHED状态:已确立的,客户端与服务器连接已确立完毕(三次握手结束)
GLOSE_WAIT:接下来代码要调用close来主动发起 fin,对方收到fin后进入该状态
TIME_WAIT:本产品给对方发起fin后,对端角色给我发fin,此时本端进入该状态,给最后一个ACK重传留有一定时间
4、滑动窗口
1、实现方法
可以在保证可靠性的其础上,提高效率(其实是降低损失,而非增加速度)
批量传输:之前先发一个数据,等待ACK,再发下一条数据;现在先发一个数据,不等ACK,再发下一个,继续下发,发了一定数据后统一等一波ACK
可靠性并非安全性,计算机中的安全往往是指通信过程中,若被恶意入侵了,是会窃取到你的关键敏感信息,安全性往住要通过加密来保证
2、丢包
1、ACK丢了
该情况无需任何处理,对可靠性无影响,无需重传
2、数据丢了
若1001 - 2000的数据丢了,返回的ack报文中确认序号就一直是1001,反复向发送方索要1001这个数据,反复索要多次是为了给1001留有等待时间
发送方在多次收到索要1001的ack报文后认为是包丢了,于是重传了1001-2000
上述重传的过程中,整体的效率是非常高的
做到了针对性重传,哪个丢了就传哪个,已收到的数据无需重传,整体效率没有额外损失的,就称为快速重传
tcp有个接收缓冲区(生产者消费者模型),发送的数据到了接收方都是先放到缓冲区里排队
5、流量控制(站在接收方控制发送方速率)
窗口大小不能无限大,可靠性是前提,任何提升效率的行为都不该影响可靠性
发送的速度不能无限大,若发的快了,接收处理不了(接收缓冲区满了)也会丢包,
与其等到满了再不传,不如提前感知到,就减慢速度,让发送方和处理方的速度保持步调一致(让接收方控制发送方的速度),这就是流量控制
16 位窗口大小,该字段来给发送方反馈发送速度,该字段仅在 ack 报文中有意义
接收方会按照自己接收缓冲区剩余的大小,作为 ack 中窗口大小的值,然后发送方就会根据这个数值来调整自己窗口的大小
当窗口大小为0时,接收缓冲区满了,此时发送方暂停发送,发送方会周期性触发窗口探测包,并不携带载荷,只是为了触发ack包,一旦非0,缓冲区又可以了,发送方继续发送
6、拥塞控制(站在通信路径上控制发送速率)
1、实现原理
核心思路:把中间路径经过的所有的设备视为一个整体
通过实验的方式,找到一个合适的传输速率
按照某个窗口大小发送数据之后,出现丢包则认为中间路径出现拥堵,就减小窗口,没出现丢包就增大窗口
按上述策略,就可以让发送速率发生动态变化
流量控制和拥塞控制谁产生的窗口更小就使用谁
2、拥塞控制实验过程
1、慢启动:刚开始传输时,速率较小采用的窗口也较小
2、若上述传输的数据,未出现丢包,说明网络还很畅通,就增大窗口大小,此时增长方式是按指数来增长,(可以使窗口快速变大保证传输效率)
3、指数增长不会一直保持,引入了一个阈值,当堵塞窗口到达阈值时指数增大,就变为了线性增长(可以使窗口持久保持一个较高的速度,且不会一下就丢包)
4、线性增长积累一段时间后,传输的速度太快,还是会引起丢包,但出现丢包,就把拥塞窗口重置为较小的值,回到最初的慢启动过程,并且这里也会根据刚才的丢包商口大小,重新设置指数增长到线性增长的國值(旧版本,已丢弃)
新版本会在丢包时把阻塞窗口设置到新的阈值,然后继续做线性增长,之后就没有指数增长了,
7、延时应答
基于滑动窗口,尽可能提高一点效率,核心在于在允许的范围内,让窗口尽可能的大
接收方收到数据后,不会立刻返回ack,而是等一会再返回
等的一会是为了给应用程序,这里腾出更多的时间来消费这里的数据,这样剩余的空间就更大了,返回的窗口就是一个相对更大的值
延时应答具体如何应答:正常每个数据都有ack,此时可以隔几个数据再返回一个ack,也能减少ack的数量,节省开销
但不仅和数据数量有关,也和时间有关,若延时时间到一定程度,即使数据没够也会返回ack
8、捎带应答
尽可能把能合并的数据包进行合并,从而达到提升效率的目的
基于延时应答,若ACK延时的时间内,响应数据准备好了,就可以把 ack 和应答数据报合并为一个TCP数据报
很多时候客户端和服务器之间是长连,要进行若干次请求
在稍带应答的加持下后续每次传输请求响应都可能触发捎带应机制,都可能把接下来传输的数据和上次的ack合二为一
9、面向字节流
粘包问题
应用程序调用read读取数据,读出数据后就要把数据转为应用层数据包,然后这个程序才能正确使用,多个应用层数据包混淆不清,就叫做粘包
粘包问题是面向字节流的都有同样的问题
解决问题的关键就是明确包之间的边界
1、通过特殊符号作为分隔符,见到分隔符就认为该包结束了
任何字符都可以,需要确保当前分隔符不会在正式数据中出现
2、指定包的长度
如在包的开始时候,加个特殊空间来表示整个包的长度
上述问题都是在咱们设计应用层协议时把该问题考虑进去
常用的应用层协议格式:xml,json,protobuffer都可以处理好该粘包问题
10、异常情况
1、其中有一方出现了 进程崩溃
进程无论是正常结束,还是异常崩溃,都会触发到回收文件资源,关闭文件这样的效果 (系统自动完成的)就会触发四次挥手
TCP 连接的生命周期,可以比进程更长一些,虽然进程已经退出了,但是 TCP 连接还在,仍然可以继续进行四次挥手
虽然说是异常崩溃,实际上和正常的四次挥手结束没啥区别,进程不在了,但是通过系统中仍然持有的连接信息,完成后续的挥手过程
2、其中有一方出现了 关机 (按照正常流程关机)
当有个主机,触发关机操作,就会先强制终止所有的进程,(类似于上述的强杀进程)
终止进程自然就会触发 4 次挥手
点了关机之后,此时,四次挥手不一定能挥完,系统马上就关闭了
如果挥的快,能够顺利挥完,此时,本端和对端都能正确的删除掉保存的连接信息
如果挥的不快,至少也能把第一个 fin 发给对端,至少能告诉对方, 我这边要结束了
对端收到 fin 之后,对端也就要进入释放连接的流程了,返回 ack, 井且也发 fin
这里发的 fin 不会有 ack 了,fin 没有收到 ack 之后,势必要进行重传..(超时重传的流程中了)
当重传几次之后,发现还是不行,还是没有 ack,这个时候,单方面的释放连接信息
3、其中一方出现了断电(直接拔电源,也是关机,更突然性的关机)
如果直接断电,机器瞬间关机,此时,肯定来不及进行发送 fin
1、断电的是接收方
发送方就会突然发现,没有 ack 了,就要重传重传几次之后,还是不行
TCP 就会尝试"复位"连接(相当于清除原来的 TCP 中的各种临时数据, 重新开始)
需要用到一个 TCP 中的"复位报文段"
RST:通过这个报文直接复位
此时的 RST 也不会有 ack
重置了还不行,单方面放弃连接
2、断电的是发送方
接收方本来就是在阻塞等待发送方的消息,结果迟迟没来消息
这个情况下,接收方需要区分出,发送方是挂了,还是好着但是暂时没发
TCP 中也是如此,接收方一段时间之后,没有收到对方的消息,就会触发心跳包来询问对方的情况
如果对端没心跳了,此时本端也就会尝试复位并且单方面释放连接了
4、网线断开
这种情况大概是 3 中的两种情况的结合
原文地址:https://blog.csdn.net/weixin_63965017/article/details/143722210
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!