JAVA EE_网络原理_UDP与TCP
人海中未遇见时,我将独自前行...
----------陳長生.
1.UDP协议
1.1.UDP协议端格式
- UDP(用户数据报协议)是由 源端口,目标端口,长度,校验和,数据 5种结构组成。
- 16位是UDP报文中字段的长度,这种设计在大多数都适用,同时保持了简洁性和效率
- 报文头一共8字节,源端口,目标端口,长度,校验和各2字节
- 源端口:标识发送方的端口号
- 目标端口:标识接收方的端口号
- 长度:表示整个UDP报文的长度,包括报文头和数据
- 校验和:用来检验数据是否出错,错误数据会直接丢弃,例如:比特翻转等..
- 单位:段
1.2.UDP的特点
- 无连接:UDP不需要与对方建立连接,只需要知道对端的IP的端口号直接传输数据包。
- 不可靠:因为没有建立连接直接传输数据,没有确认机制和重传机制,所以不知道对方有没有接收到数据,如果因为网络故障导致数据包没法成功发送,UDP协议也不会报错。
- 传输速率高:因为不知道对方有没有接收到数据,没有确认机制和重传机制,所以UDP就不管对方有没有接收数据,直接发送该段数据,大大提高传输速率。
- 面向数据报:不能够灵活控制数据的发送数量与次数,只能作为整体进行发送。
- 不保证顺序:因为数据报独立于路由,所以每段数据都有自己的路径,这就导致了数据不能按顺序抵达终点。
1.3.理解UDP的“不可靠”
可以将UDP协议理解成为快递卸货,卸货人员不管三七二十一,只要是该目的地的快递包裹随便拿一个就直接往后丢,不管后面的人有没有接(无连接,速率高,不保证顺序)。
1.4.校验和
发送方构造UDP数据报,将数据的每个字节进行相加,最后组成一个16位的数据,此时得到的结果就为校验和check1,等接收方收到UDP数据报之后,就会按照相同的算法,计算一遍校验和check2,这时将check1和check2进行比较,如果相等则正确,不相等则错误。
1.5.面向数据报
应用层发送多少数据给UDP,就按多少数据发送出去,不能拆分也不能合并
当接收到100字节的数据时,不能分10次10字节或2次50字节发送,只能一次性发送100字节
1.6.UDP使用注意事项
因为一段UDP数据的长度最多为64K(65535字节)[1K=1024字节],这对于现在来说,是个很小的长度,所以传输大量数据的时候要在应用层进行切片和重组(即当我们使用UDP的时候要分多次数发送,并在接收端手动拼装)。
一般用于可以接受数据丢失的场景,例如学校机房...
那按理来说,将每个结构的两个字节改为四个字节不久能提高数据长度了吗,为什么不扩展一下呢?
因为这个操作很难,世界上任意两台设备都会可能使用UDP进行通信,如果一旦进行升级,那么就会又一部分设备为更新过的,另一部分则为旧的,新旧设备无法正常通信,可能会造成很大的事故~~
2.TCP协议
2.1.TCP协议格式
- 16位源端口:发送端的端口号
- 16位确认端口:接受方的端口号
- 32位序列号:用于标记发送数据的顺序
- 32位确认号:用于标记应答发送数据的顺序
- 4位首部长度:32位字(即4字节),如果有一段值为5,那么该首部长度就为4*5=20字节
- 保留6位:用于未来扩展,例如:窗口扩大因子,时间戳...
- 16位窗口大小:用于流量控制
- 16位校验和:使用CRC算法检查数据是否正确
- 16位紧急指针:用于存储紧急数据
- 选项:提供一些额外的功能,例如:窗口扩大因子,时间戳...
- 数据:用于存储发送内容
- 6个序列号:
- URG:紧急报文(紧急报文可插队传输)
- ACK:应答报文
- FIN:结束报文
- SYN:同步报文
- PSH:催促报文(就像老师催作业)
- RST:复位报文(单方面断开连接)
2.2.确认应答
基于TCP是与对方建立连接,并且可靠的协议,所以会有一个确认应答的机制。
客户端每发送一条数据都会产生一个序列号,并且服务端会根据客户端的序列号产生一个确认序号与之对应。
为什么要有序列号来确认应答呢?因为网络中会产生后发先至,在网路中,数据包可能会选择不同的路由路径来传输,那么先发送的路由包可能会选择一个较长且拥塞的路径,而后发的一个数据包可能会选择一个比较通畅的路径,那么这两条数据就会产生后发先至。
例如:
正常情况:
后发先至情况:
就造成了错误的结果,长生就认为小美爱吃香菜,其实小美是不爱吃香菜的~
加了序列号后:
2.3.超时重传
在网络中,一些数据包的发送可能会超出最大时间数,而超出最大时间数还没传输成功的数据包会被系统丢弃掉。而在一些网络堵塞的情况下,就很容易导致丢包现象发生,那么TCP就提供了超时重传机制。
情况一:
情况二:
在以上情况中,服务端会接受到重复的数据,那么TCP如果删除这些重复的数据呢?我们前面提到过序列号,TCP根据序列号就很容易进行去重操作,就很容易的解决了这个问题。
特定时间设定:不要太长,也不要太短,设定一个中间值*2即可
假设长生送妹妹去补习班上课,但是长生忘记了妹妹今天有没有课,到了校门口,就跟妹妹说:你进去看看有没有同学和老师,如果没有就出来跟哥哥回家。随后妹妹就走了进去,那长生肯定也不能在外面死等啊,于是就估计了妹妹从校门口到教室的时间大概是6分钟,那么就计算一个来往路程时间,等个12分钟,如果12分钟后没出来,那么今天就要上课,如果出来了,今天就没上课。
2.4.连接管理
TCP中重要的便是“三次握手”和“四次挥手”
正常情况下,TCP要经过“三次握手”来建立连接,“四次挥手”来断开连接
三次挥手:
客户端和服务端都处于初始状态,服务端等待客户端发来消息,客户端给服务端发送建立请求信息,服务端接收到客户端的请求后,发送应答和建立连接请求消息给客户端,客户端接收到后就发送应答消息返回,此时两端建立连接,可以相互发送数据。
就类似于军旅电视剧中用对讲机通信:
四次握手:
2.5.滑动窗口
滑动窗口就是一个固定大小的空间向前滑动,就像现实中的窗户。
正常的数据包传输是由客户端与服务端进行一发一收的操作,虽然可靠性提高,可是这样速率和性能就大幅,那么我们就可以用滑动窗口来进行批量数据的传输。
- 固定数量进行传输
- 接收到一个ACK值,滑动窗口就滑到下一个位置,传输该值,如何继续滑动
- 系统会为其分配一个“发送缓存区”来确认哪条数据发送完,然后滑动窗口就将哪个值丢弃
- 窗口越大,吞吐量越大
批量数据的传输肯定会出现丢包的情况
情况一(客户端的应答包丢失):
当客户端接收三次重复数据时,就会重新发送丢包的那条数据
情况二(服务端的数据包丢失):
服务端发送的ACK中,会包含前面所有ACK内容,所以即使前一个ACK丢失,但是后面一个ACK成功发送,也不会影响。
2.6.流量控制
服务端处理数据的能力是有限的,当客户端发送过多的消息给服务端,就可能导致服务端的缓存区被冲垮,造成大量丢包的现象。
于是TCP就提供了流量控制的机制,将数据由少量转多量进行发送,当服务端的缓存区到达极限时,就会停止发送ACK返回给客户端,当缓存区中的数据缓解时,就会发送试探窗口给客户端,让客户端继续发送数据给服务端。
2.7.拥塞控制
虽然流量控制能提高可靠性,但是相对速率来讲还是提升的较少,那么TCP就由“拥塞控制”这个机制,从可靠性和速率取中值来进行数据包传输。
慢启动 :
拥塞窗口从1开始增长,直到达到慢启动阈值
拥塞避免:
当窗口超过阈值时,增长的方式变为线性增长
TCP Tahoe:
- 当数据包丢失时,会将拥塞窗口重置为1,并重新进入慢启动阶段
- 效率低,每次丢包时候都会降低发送速率
TCP Renp:
- 当检测到数据包丢失时,不会将拥塞窗口置为1,而是将ssthreh值设为拥塞窗口的一半,并将当前拥塞窗口置为新ssthreh值,再进行拥塞避免
- 效率高,减少了再丢包时产生速率的下降
我们可以把拥塞控制的过程理解为恋爱,两个相遇相爱,很快到了热恋期,感情直线上升,热恋期过后,感情就缓缓上升,但随着两个人越来越了解,彼此的缺点也放大之后,两个人的感情到了临界点,受不了了,就会吵架(TCP Renp),甚至分手(TCP Tahoe),但过了冷静期之后,两个人又淡化了对方的缺点,甚至包容,又想起了对方的好,于是两人又和好,恢复了之前的感情~~
2.8.延迟应答
- 如果接收到客户端的数据就立刻返回ACK消息,那么返回的窗口大小就会很小
- 假设接收缓存区为1M,如果一次收到了500K的数据,那么立即返回的数据就是500K
- 处理的速度很快,实际上可能5ms就处理完500K的数据
- 但是500K还没到接收缓存区的极限,即使数据再大些也能处理得过来
- 那就可以延迟一下,等个10ms,这样窗口的数据可能就为1M
- 窗口越大,吞吐量就越高,速率也就也高
- 延迟应答就是为了合理利用窗口大小来提高吞吐量
举个栗子:
疫情上网课时候,老师会布置线上作业,但是长生落下了8项作业没写,这时候班主任就发消息问长生为什么没做作业,那么长生这时有两个选择
选择一(立即应答):
直接回答:老师,我会补上的。
选择二(延迟应答):
先冷落班主任,然后狂补8项作业,补完后跟老师讲:不好意思老师,我写了忘记交了。
2.9.稍带应答
服务端接收客户端的数据时,正常应该直接发送一个应答ACK数据包,但由于TCP会延迟应答,这个ACK就会延迟一段时间发送,而这一延迟就到了服务端的返回数据包发送给客户端,那就正好两个数据包一起发送。
稍带应答会将ACK和要返回的数据包一起发送
长生平时比较懒,中午吃完饭之后,碗就放在水池中没洗(ACK),然后到了晚上时候,又有碗(要返回的数据包),此时水池的碗筷正好快堆满了,那么长生就把两次的碗一起洗了(捎带应答)~~
2.10.面向字节流
- 由于TCP内容有一个缓存区,所以TCP的读和写不需要一一匹配
- 当TCP读取一个100个字节的数据,他可以直接写100个字节数据,也可以分100次写1个字节数据
- 读取100个字节的数据时,即不用考虑如何读也不用考虑如何写,可以一次读所有字节数据,也可以分批次读取其中的字节数据
2.11.粘包问题
粘包问题中的”包“是应用层的数据包,是指会将几个小段的数据粘成一给数据包,导致接受端区分不了原来的数据。
TCP分批次的传输数据,最后都会到达接收端的”接收缓存区“,”接收缓存区“会将这些数据处理为一整串字符串——”aaaabbbbcccc“,那么到应用层的视角就看不出来原来的数据是怎样的,可能乱解读,一个字节一个字节或者是两个字节两个字节,就会导致传递错误的信息给用户。
解决方法:
- 在发送数据的开头规定读取多少个字节数,那么应用层就直到该如何读取
- 在每段数据的结尾设定一个分断号
- 在数据的开头和结尾设定一个定界符(用的少)
2.12.异常问题
进程奔溃
- 进程奔溃表示对应的进程被关闭,而TCP不会因为进程的关闭而立即关闭,而是会保留一会,属于正常开关软件的情况。
正常关机
- 正常流程下的关机,会将所有线程先关闭,然后进入四次挥手状态
- 如果关机的比较慢,那么四次挥手很可能会执行完
- 如果关机的比较快,那么四次挥手不会被执行完
- 对端会正常放回ack和fin,但是传送的fin并没有得到回应,出发了超时重传之后也如此,那么对端就会直接断开连接
主机掉电(拔电源)
- 以台式机为例,拔掉电源后直接强制快速关机,根本来不及进入四次挥手状态
- 如果掉电的是接受端,发送方会反复发送信息,但没有ack返回时,会触发超时重传机制,到一定程度后,就会发送一个复位报文,断开连接。
- 如果掉电的是发送端,那么接收方会感受到对方突然没法消息,不知道是对端情况,可能是断电,也可能只是短时间内不想发消息,就会进行阻塞等待,那么肯定不能这么死等下去,于是就会触发TCP的”心跳包“,来判断对端是否还在。
- 心跳包:发送端会周期性(时间根据实际情况确定)发送一个没有内容的数据包,接受发会返回一个ack表示我还在,如果没有返回则表示接受发挂了~~
网线断开
- 与主机掉电同理