文章目录
- UDP协议
- UDP协议数据报
- 报头
- TCP协议
- 确认应答
- 缓冲区
- 超时重传
- 三次握手
- 其他问题
- 四次挥手
- 滑动窗口
- 流量控制
- 拥塞控制
UDP协议
前面我们只是说了UDP协议的用法,但是并没有涉及到UDP协议的原理
毕竟知道冰箱的用法和知道冰箱的原理是两个层级的事情
我们首先知道计算机网络世界是搭建在四层架构上的
而HTTP协议是处于最顶层,是应用层协议,应用层协议的最大特点就是非常多,而且各异
这样多的协议要在网络中传输,必须得给他统一了,并且还能将底层收上来的数据,正确的交付到各个端口中
做到这些的就是传输层协议,主要有两个,就是大名鼎鼎的UDP和TCP
UDP协议数据报
所有的协议都规定了两部分,就是报头和数据本身,在传输层我们一般习惯把这整体称之为数据包
报头
报头是这样的
相比于IP协议和TCP协议,UDP协议的报头还是十分友好的
UDP的报头大小是固定的,8字节,因此当我们获取到一个UDP数据报之后,取前8个字节,找到UDP数据报的总长度,就能完整的取到整个报文数据
需要注意的是,16位UDP长度指的是UDP数据报的总长度,包含报头和数据部分,因此UDP的最大数据大小就是2^16-1,大小就是64KB
UDP的传输过程是不可靠的,无连接的,面向数据报,在我们之前介绍的时候有说过,他的主要应用场景其实就是直播了
TCP协议
TCP报头就比UDP丑多了,而且他还是不定长的,其中有一个交4位首部长度,是代表了TCP报头的大小,范围是20到60字节
其他的部分都是用来确保TCP的可靠性和效率所用到的
TCP如此知名,就是因为他的可靠性,那么他做了哪些事情保证他的可靠呢
确认应答
我们发出了一条信息,怎么确定对方是否看到了呢,在Line或者抖音中,会回显对方是否已读,这其实就是一个确认应答机制
为了保证可靠性,TCP协议规定了ACK机制,也就是确认应答机制
机智的朋友肯定发现上面的标志位中有一个ACK,就是用于这个事情的
但是如果服务器和客户机一人一条发送,服务器每发送一个数据,都要等客户端回答收到之后再发送,这样固然是可靠了,但是效率却也大大降低了
于是就有了下面的想法,一次发送10条数据,分别标记上1到10
客户端收到1回复2,表明自己的1收到了,下一个想要2,因此客户端在一次收到1到10之后会分别回复2到11
但是计算机网络纷繁复杂,数据报可不一定是按顺序到达的,这就麻烦了,我怎么知道我缺哪个呢,而且每一个都进行回复也太二了
然后我们再想,一次发送了1到10,但是接收到了,1到5,8到10,6和7都丢了
那我们只回复5,标识5以前的都正确收到了,接下来想要6
这样就好很多了
缓冲区
除此之外,TCP的协议是全双工的,用一个端口就可以执行发送和接收两个操作,而且系统调用recv和read也不是从网卡中读取数据到内存,而是从缓冲区里拿上来的,send和write其实也算写入到缓冲区的,不是直接写到网卡里
那这个缓冲区写满了怎么办,怎么知道,发送缓冲区没数据了怎么办
这其实就是那16位的窗口做的事情,他分别对应了缓冲区的大小,每一次收发其实都会把缓冲区的状态写在里面,当缓冲区都快满了,写方就知道不要再往里面传了
超时重传
当数据在传输过程中丢了怎么办,迟迟没有收到ACK就说明发送失败了
当服务器等了一段时间也没有收到客户机发来的ACK,就说明数据可能是丢了,无论是数据丢了,还是ACK丢了,都会触发超时重传
这时候TCP协议就会要求服务器重新传一次数据
一般来说这个一段时间其实是动态的,各家操作系统都是这样
逻辑是这样的500ms是一个单位,每次乘2,当次数有几次之后,就说明对方主机可能出毛病了,有可能是被拔网线了,这时候就不会重传了
其实TCP协议他可靠吗,确实,在他能做到的范围内确实可靠,但是如果被拔网线就没办法了(不可抗力)
三次握手
我们说TCP协议是面向连接的,这个连接是怎么建立的呢
就是通过三次握手,在TCP报头中的SYN标记就是标识我要跟你交朋友
过程是这样的
客户端发起请求,说,我要跟你做朋友(发送一个包含SYN标记的报文)
服务端收到之后,说,我收到了你的消息,我也要跟你做朋友(发送了一个ACK和SYN标记都有的报文)
客户端收到之后,说,好!(发送一个ACK标记的报文)
这三次数据传递其实就建立了一个TCP连接,但是建立连接的时候,是在哪一个动作呢
其实是在客户端最后一次发送之后,客户端就认为连接建立好了,而服务器接收到了之后,服务器就认为连接建立好了
接下来客户端就可以发送请求了,疯狂星期四,V我50
需要注意的是,服务器可不是一次只跟一个客户机聊天,说不定有成千上万的客户端来请求,而操作系统的管理策略其实就是先描述再组织,将这些连接管理起来
其他问题
有一个经典的面试问题为什么是三次握手,其他次数行不行
- 偶数次
这里需要知道一点,当我发出一条消息的时候,我是不知道这条消息能不能传达到的,但是可以确定的是,我之前的消息一定传到了,并且我也可以收到对方的消息
而在这个过程中,永远是客户机给服务器发送请求,如果是奇数次,说明最后一个确认是服务器发给客户端的,说明之前的信息都没问题了,为什么还要继续确认呢?我直接发我的请求不好吗
而且如果使用偶数次握手,是服务器先确认建立的连接,客户端就可以一直发送SYN报文,一直不建立连接,服务器需要面对的可就多了,维护连接过多可是会挂掉的
- 其他奇数次呢
1次就不说了太蠢了,5次以上那不就是浪费资源了
3次就能干好的事情为什么要5次7次,那不是脱裤子放屁吗
四次挥手
有资源的申请就要有资源的释放,有链接的申请就要有链接的释放
在TCP报头中有一个叫做FIN,其实就是final,标志着我要离开我的朋友了
链接的释放可以说客户端也可以是服务器,这里我为了方便表示说是客户端,表示我要的资源已经拿到了,要拜拜了
客户端发出请求,要拜拜了(发送一个带有FIN的报文)
服务器收到了,我知道了(原地等待一会儿)(返回一个ACK,表示我知道了,然后等待一个CLOSE_WAIT的时间,给客户机反悔的机会,看客户机还有没有别的话说)
这段时间服务器什么也没有等到,服务器说,这是我跟你说的最后一句话,以后再也没有了(假),拜拜(发送了一个LAST_ACK,表示最后一个ACK报文,并且附带了FIN标志,表示结束)
当客户端收到之后,其实连接就已经断开了,并且会维持一段时间TIME_WAIT,不让客户端对同一个端口发送请求,咱不能抓着一只羊薅羊毛吧
滑动窗口
如果服务器发送了1到20号数据,但是客户端收到的是1和3到20,只发了一个2的请求,服务器看到之后觉得他只收到了1,于是把2到20又发了一遍,这样的效率又变得不行了
于是就有了滑动窗口,我们把发送缓冲区和接收缓冲区想象成数组,儿窗口限制的其实是左右的下标,我们每次只确认窗口中的数据即可
需要注意的是,在滑动窗口中的每一个部分其实都是需要确认ACK的,这是和之前不一样的
流量控制
流量控制其实用到的原理就是上面的滑动窗口,我们需要控制发送数据的速度,不能让接收端的缓冲区过满,不然就是无用功了
这时候TCP报头中的显示缓冲区情况就起到作用了
拥塞控制
拥塞控制与流量控制不同,他是为了防止网络状况不好产生的原因,比如说路由器出问题,网络拥堵送不出去
TCP的解决方案是慢启动,他指的是一开始的发送的数据很少,但是是指数级别的增长
当这个增长达到一定阈值之后,就是用线性增长了,如果遇到了重传,就会减半