传输层的UDP协议(User Datagram Protocol)
职责
在网络层的基础之上,实现了进程与进程的通信
UDP协议的包头信息
UDP添加的封装(解包,分用)
![](https://i-blog.csdnimg.cn/blog_migrate/0bd07eafbd8553cf9096b6fe58f04a2e.png)
UDP的包头是一个定长的包头:容易做解包
源端口 + 目的端口:做分用 | 进程 到 进程
校验和:防止数据错误的,类似hash的算法,
发送端:有效数据hash(有效数据) ——> h1
将得到的hash值填入校核和部分
接收端:有效数据hash(有效数据) ——> h2
h1 != h2 : 数据一定出错了(直接丢弃)
h1 == h2 : 大概率认为数据还是源数据
UDP发送的工作流程
1.填充端口
2.计算长度 + 填充长度
3.计算校验和 + 填充校验和
4.立即将数据交给网络层
ps:UDP协议栈内部没有发送缓冲区
UDP接收的工作流程
1.从网络层接收到数据
2.通过计算校验和,检查数据是否出现错误,如果出现错误直接丢弃
3.数据没有问题,根据自己内部维护Map<port,pid>找到对应的接受进程,如果找不到对应的进程,就丢弃
4.看对应的进程是否准备好一块内存,如果暂时没有,需要把数据暂时保存一段时间
5.把数据复制到对应的内存空间 byte[] buf
ps: UDP协议内部有一个接受缓存区
UDP的特点
1.不可靠
2.无连接
3.面向数据报文
4.全双工
抓包
![](https://i-blog.csdnimg.cn/blog_migrate/98b0cefcd95ba6545c2ffaeb4438236e.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/b855e34dda5b1ee1d674472395a8772e.png)
0x270f 源端口号
0x1f90 目的端口号
0x0024 UDP长度
0xfda2 校验和
传输层的TCP协议**(Transmission Control Protocol 传输控制协议)
职责
进程 to 进程
可靠(承诺的是可靠,从没有承诺过安全)
TCP协议的包头信息
如何解包,如何进行分用
![](https://i-blog.csdnimg.cn/blog_migrate/fca20ee80ee1c11238ae24657edc7b39.png)
TCP包头是一个变长的包头,找到包头长度后我们可以进行解包
源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
32位序号/32位确认号:后面详细讲;
4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是
15 * 4 = 60
6位标志位:
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN:请求建立连接;我们把携带SYN标识的称为同步报文段FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
16位窗口大小:后面再说
16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光
包含TCP首部,也包含TCP数据部分。
16位紧急指针:标识哪部分数据是紧急数据;
40字节头部选项:暂时忽略;
TCP的保证“可靠性”
什么是可靠?
TCP会尽自己最大的努力,将数据发送给对方
如果真的遇到发送不过去的情况,TCP至少会告诉发送进程,数据发送失败了
保证不会收到错误的数据(通过校验和)
TCP能保证收到的数据是有序的(按照发送进程发送时的数据)
TCP会根据对方的接受能力和网络线路的承受能力,进行流量的控制
TCP做了哪些机制来确保可靠性
确认应答机制:接收方(对方的TCP)有责任对收到的数据进行确认(acknowledge)应答
如果同时收到多份数据,如何知道应答的是哪份数据呢?
我们应该对数据进行编号——序列号,也就是TCP包头中的32位序列号,它会携带本次发送时的数
据的编号,某个字节占用一个序号,这个序号就是本次发送的数据的·第一个字节的序号
比如:开始序号从1开始
[abcd] SN = 1
[efgh] SN = 5
确认段:一份数据既可以起到发送数据的角色,也可以起到确认的角色
发送:携带有数据,填写正确的SN,就是发送确认段
确认:标准位ack置1,代表起到了确认的作用。需要填写确认序列号
确认序列号:填写下一次期望收到的第一个字节的编号,换言之就是,确认序列号之前的数据已经 全部收到了
例如: 1.发送[abcd] SN = 1
2.确认ack = 1 ASN = 5
![](https://i-blog.csdnimg.cn/blog_migrate/0bd9bad3eec4cc88e7698985b7eded99.png)
Tips:TCP维护着各自的发送SN,也就是说TCP的发送序列号是独立的
回到发送端(我们没有上帝视角)
当我发送了一份数据,没有收到应答时,可能发生了什么?
可能情况:
对方没有收到
![](https://i-blog.csdnimg.cn/blog_migrate/7dd726ae44bd01200da128dc22e5af00.png)
对方收到了,并且应答了,但应答没有发送过来
![](https://i-blog.csdnimg.cn/blog_migrate/82a238097596d15c8b6363f473ce12ef.png)
主机B收到过2次1~1000,所以主机B,需要有能力
直到遇到没有收到应答(超时重传机制)
超时timeout机制
不应该无限期的等待下去
重新发送数据
TCP的发送端不用关心超时没有收到应答的原因是什么,采用统一的超时重传机制即可,如果接收端真的收到了重复的数据,直接丢弃即可。
超时重传是无限制的吗?
会有一定的上限,达到上限,发送方(TCP)认为本次数据线路出现了重大问题
TCP会关闭本次连接
TCP会通知进程(在Java中,采用异常方式IOExpection)
TCP会发送一个reset segment 出去
关于超时时间的设置
一般这个时间不是一个固定的长度,大多数是逐步变长
10s ->20s -> 40s -> 80s
站在进程角度思考,向一个有问题的TCP线路中发送数据,多久之后,进程会知道线路有问题了
10 + 20 + 40 + 80s 之后,发现
作为TCP的发送方,经过一段时间之后,是可以知道线路有问题的!
作为TCP的接收方,无法得知线路是有问题的(无法确认对方是没有数据发送还是发送失败了)
小结
TCP提供了 确认应答机制(数据编号机制 + 超时重传机制),保证了
TCP在尽自己最大的努力,把数据发送给对方
TCP在最终发送失败之后,通知了进程发送失败的事实
涉及TCP包头:
TCP一个segment,既可以当发送segment,也可以当确认segment使用。
SN + 携带数据(payload) -> 发送作用
ack 标志位 + ASN -> 确认作用
SN:本次数据的第一个字节的编号
ASN:希望下次发送的第一个字节的编号
连接管理机制
TCP有发送缓冲区,发送之后不可以直接丢弃,因为可能要重发,所以至少要一个地方保存这些数据。
TCP有接受缓冲区。
TCP需要维护发送时的序号 SN = x ,才可以用于发送时填充SN字段
TCP 还需维护已经接收的数据的序号 ASN = y,才可以进行去重
为什么TCP要设计建立连接
必须确认对方存在,才能可靠的传输
交换一些必要的数据(SN不是直接从1开始的,会双方各自生成,随后需要交换)
所以在正式通信之前,需要一个阶段:
确认对方在线
同步一些基本信息
TCP大体分为3个阶段
【建立连接阶段】 【正式通信阶段】 【断开连接阶段】
拨电话 打电话 挂电话
TCP的状态:表示当前连接目前地状态
建立连接阶段
主动连接方(打电话的角色) 大部分是Client
被动连接方(接电话的角色) 大部分是Server
主动连接方 确认对方在线 + 告诉对方自己的信息
被动连接方 进行应答
被动连接方 确认对方在线 + 告诉对方自己的信息
主动连接方 进行应答
(2和3基本是同一时间发生的,TCP的segment可以既承载发送数据,又承载应答的职责)
结论:2和3是会被合并的也就是三次握手
TCP的三次握手过程
![](https://i-blog.csdnimg.cn/blog_migrate/7589a7bc02a8b5ab155b0101ecaa08af.png)
segment的种类:发送segment,确认segment,同步信息(握手阶段使用)segment,挥手segment
![](https://i-blog.csdnimg.cn/blog_migrate/a9cdb0b459573e3bbc6ee7d3b9d99cb6.png)
ack = 0 没有确认的意思 ack = 1 有确认的意思
syn = 0 没有同步的意思 syn = 1 有同步的意思
fin = 0 没有挥手的意思 fin = 1 有挥手的意思
TCP的状态变更角度
![](https://i-blog.csdnimg.cn/blog_migrate/90cec8c10f8d53ba3681ed0644f69a8f.png)
establish:建立连接
closed:虚拟状态
listen:被动连接方(服务器已经启动,但没有真正的建立连接)
syn_rcvd:收到了sync
syn_sent:sync已经发送了
流程图:
![](https://i-blog.csdnimg.cn/blog_migrate/e358648d1c7521f271f08f62da16691c.png)
从代码角度观察TCP的状态变更
codeA
![](https://i-blog.csdnimg.cn/blog_migrate/2ea76a4a8912330ad070180510ae7c3e.png)
codeB
![](https://i-blog.csdnimg.cn/blog_migrate/8a52628a5417f60820758608b738acdf.png)
codeC
![](https://i-blog.csdnimg.cn/blog_migrate/1e246f3e4d3bcb9e1e6a7060ecda20f7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/30be2a1c37cce81320fd34a1b7f7c8a6.png)
PS:
codeC 的方法(构造)会一直阻塞,直到3次握手阶段,换言之,应用层是看不到三次握手的细节过程