写在前面
本文来看下TCP和UDP协议。
我们接触这两个协议最多的应该就是在面试中了,经典题目就是“TCP和UDP有什么区别?”,而最常得到的答案就是TCP是面向连接的,而UDP是面向无连接的。
那么这里的连接
到底是什么呢?难道真的是有一条物理上的线在那里吗?当然不是,实际上是这样子的,我们知道,TCP想要建立连接,必须经历三次握手的过程,三次握手其实就是在交换数据,而三次握手之后,双方都会将这些TCP协议本身需要用到的数据使用一定的数据结构维护起来,所以连接其实就是使用一定的数据结构保存的数据
,也所以连接其实就是数据了,即连接=数据
。那么UDP为什么是无连接的呢,因为它不需要维护这些数据。使用这些数据来维护交互的状态,也就是在逻辑上连接起来了,而绝非物理上的。
TCP维护连接状态的数据都有哪些呢,比如滑动窗口信息,哪些数据发送了没有ack,哪些数据发送了还没有ack,哪些数据还没有发送等。
其他区别:
TCP提供可靠交付,UDP不是
TCP是面向流的,没头没尾,而UDP是基于数据包的,一个个发,一个个收
TCP有拥塞控制,UDP没有
所以啊,可以总结来看,TCP也是一个服务,而程序分为无状态的服务和有状态的服务,很明显TCP是属于有状态的服务了。相对应的UDP就是一个无状态的服务。
1:UDP
UDP全称,user diagram protocol,即用户数据报协议,是一种无连接协议。
1.1:UDP的头
当机器收到一个包之后,物理层首先会拆掉物理层头部,判断MAC地址是否匹配,如果是匹配则交给网际层,网际层获取头部后,发现IP地址匹配,则交给传输层处理,但是到底是交给哪种传输层协议呢,这里需要看IP头部的具体存放使用的是TCP还是UDP的8位协议信息了,这里假定是UDP,数据到达UDP,UDP怎么知道交给哪个应用程序呢?这就需要端口号了,也就是UDP头的如下信息:
这里也可以看到UDP的头只有8个字节,简单到一塌糊涂。而相比之下TCP的头就要复杂多了。
传输层处理完毕之后,内核的工作也就完成了。接下来只要找到监听了目标端口号
的应用程序,交给其处理就行了。
1.2:UDP适用的场景和实例
1.2.1:网络比较好的内网环境
比如像飞鸽,飞秋等内网的聊天工具。
1.2.2:对时延敏感,且对少少量丢包不敏感应用
比如实时对战游戏,属于分秒必争型的,很多职业玩家,为了进一步降低时延甚至会买专业版的鼠标和键盘。如果是使用TCP,可能为了等待上一个没有到达的包,早就被爆头了。
比如直播场景,视频网站,少量的丢包用户其实是没有感知的,特别是直播追求实时性,已经丢失的包就算等到了其实也没有意义了。
1.2.3:应用本身资源少
比如IOT物联网,设备内部可能只是嵌入了一个很小的操作系统,终端系统资源很少,而TCP需要额外存储一些维护连接状态的数据,协议代价比较大。
1.2.4:广播场景
比如DHCP。就算丢包也所谓,重发就行了。
2:TCP
TCP相对于UDP复杂多了,因为TCP要实现流量控制,顺序控制,拥塞控制等,而想要实现这些控制,就需要额外存储相关数据,这些数据自然需要保存在头中,所以,这部分我们先从TCP的头看起。
2.1:TCP头
如下:
- 源端口和目标端口
没得说,最低配了,UDP就是如此。 - 序号和确认序号
序号是给包指定顺序的,解决乱序问题。确认序号是用来进行确认哪个序号的包已经收到的,解决包丢失问题。
需要注意:确认序号是来确认哪个序号的包已经收到的,其和序号本身具有顺序是没有任何关系的,
即就算序号本身是随机顺序对确认序号也是没有丝毫影响的。
- 常用的状态位们
URG:紧急位,需要被优先发送的包,比如要下一个大文件,但是下载过程中突然取消下载,这个取消下载的消息就应该紧急发送到应用层。
ACK:确认包位,该位置为1时代表时一个确认包
PSH:推送
RST:连接重置包位,跳过四次挥手,直接断开连接
SYN:三次握手建立连接位
FIN:四次回收断开连接位
注意这些状态位的发送会引起双方状态的变更。
- 窗口大小
做流量控制,用来告知对方自己的处理能力,别发太快了,也别发太慢了,即改用什么速度来发送数据,就是流控了。 - 校验和
UDP也有,最低配了,校验数据的完整性。
2.2:三次握手
- 为什么是三次?
既然要建立连接就要确定对方是在的,至少在自己发消息的那一刻是在的,而要确定是在的,就至少需要一个来回,如下:
1:A给B发消息,此时B知道A是在的
2:B给A回消息,此时A知道B是在的
欸,这么看起来2次握手就够了,但,握手的目的是要建立连接,而建立连接的本质就是数据的交换,这里交换的数据呢就是数据的序号了,当然这个不必过分关心,只要知道是要交换数据来构建维护连接的状态的数据结构
就行了。因此不仅要确定对方在,还要确定自己发给对方的数据对方收到了,所以过程就变为如下这样:
1:A给B发消息并携带数据,此时B知道A是在的
2:B给A回消息并携带数据,此时A知道B在的,并且收到了自己的数据
3:A给B回消息并携带数据(此时其实就可以携带业务数据了),B就知道A收到自己的数据了
所以至少需要三次握手。你可能会说万一三次握手之后服务挂了咋办,这咱就控制不了了,只能是尽人事,听天命
了。参考图:
2.3:四次挥手
按照正常的逻辑断开连接的过程应该是这个样子的:
1:A给B说,我要断开
2:B给A回,好的,我也断开了
这样子其实是有问题的,因为B不能直接断开连接,原因是这个关闭的动作应该由上层应用来被动完成,而非主动完成,因为上层应用还要释放资源等。所以这个过程要变成如下:
1:A给B说,我要断开
2:B给A回,好的,我已经通知上层应用了,等上层应用关闭我再通知你
3:B上层应用调用关闭方法,B再给A回,上层应用已经关闭了,我关闭了啊
4:A给B回,好的,我收到了
这个时候B已经关闭了,但是A还不能关闭,因为B的包可能还在路上,所以要等待所有的包都死翘翘了才关闭,这个时间时2MSL。如果是A很快将端口释放出来,则来自B还在路上的包可能会被新占用了该端口的新应用收到,出现问题,当然现在可以通过时间戳等技术直接识别并丢弃端口上一任主人
的包,但这里就相当于是上了个双保险了。
为啥B不用等2MSL呢?因为A主动关闭,这个时候A不会再发业务包出去了,而在四次握手时,因为上层应用还没有释放资源和调用内核的关闭,所以上层应用还是有可能发出新的包的,所以A要等,而B不用等。
为啥是2MSL?一来一回的时间,A主动关闭前可能刚给B发了包,此时A发给B,B响应A,刚好2MSL。
这个过程如下图:
2.4:TCP状态机
看图重要
:
2.5:消息重传和确认
参考这里 。
2.6:滑动窗口
参考这里 。
2.7:拥塞控制
参考这里 。
写在后面
参考文章列表
多知道一点
网络包的单位都有哪些?
网络传输是以包为单位的,二层叫帧,网络层叫包,传输层叫段。我们笼统地称为包。