大家好,又见面了,我是你们的朋友全栈君。
深度理解TCP/IP
1.TCP基础知识
1.1 什么是TCP?
-
TCP 是⾯向连接的、可靠的、面向字节流的传输层通信协议
-
面向连接:只能一对一连接,不能一对多
-
可靠:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证⼀个报文⼀定能够到达接收端(依靠各种机制)
-
字节流:消息是没有数据边界的(管道也是),不管消息多大都可以传输,并且消息是有序的
1.2 什么是TCP连接?
- ⽤于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接
- 所以一个TCP连接是需要客户端和服务端达成三个信息的共识:
- Socket:IP地址 + 端口号
- 序列号:用来解决乱序问题
- 窗口大小:流量控制
1.3 TCP协议段格式
- 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去
- 32位的序列号:占4个字节,TCP是面向字节流的,所以在每一个TCP连接中传送的字节流的每一个字节都是按顺序编号,整个要传送的字节流的起始序号必须在建立时设置,通过SYN包传给接收方,
主要解决网络包乱序(去重)的问题(接收方更加关心)
- 32位的确认应答号:占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号,比如说:A给B发送了一个 6 7 8,B返回的应该是 7 8 9,如果A只收到了一个9,那就说明9之前的所有数据B都已经正确收到,
主要解决不丢包的问题(发送方更关心)
- 4位首部长度(也有叫数据偏移):表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60
- 6位保留:保留为今后使用,目前应置为0
- URG(紧急:URGent):当URG = 1时,表示当前报文段中存在优先处理的数据,也叫
带外数据(OOB:out of band)
,不要按原来的排队顺序发送,会把数据紧急插入到本报文段的最前面,这时就和后面的的16位紧急指针配合使用,可以理解为一种数据的插队机制
- ACK(确认:ACKnowledegment):仅当ACK = 1时,确认号字段才有效,ACK = 0时,确认号无效
- PSH(推送:Push):提示接收端应用程序立刻从TCP缓冲区把数据读走,比如:A和B正在通信,A端的一个进程希望立刻获得B端的回应,这时A端就把PSH置为1,立即创建一个报文段发送出去,B端收到后,尽快交付给上层的进程,不需要等待缓冲区填满再向上交付
- RST(复位:Reset):RST = 1时,说明TCP连接出现了问题,必须释放连接,然后再重新建立连接,RST还可以用来拒绝一个非法的报文段或者拒绝打开一个连接,RST也可以叫做重置位
- SYN(同步:SYNchronization):在连接建立时用来同步序号,当SYN = 1,ACK = 0时,说明这是一个连接请求报文段,如果对方同意,在响应报文段中SYN = 1,ACK = 1
- FIN(完结:Finis):用来释放一个连接,当FIN = 1时,表示数据发送完毕,并要求断开连接
- 16位窗口大小:占2字节,窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口),窗口值会告诉对方:从现在开始,我只要多少的数据,是因为接收方的缓冲区大小是有限制的,
窗口字段明确指出了现在允许对方发送的数据里量
- 16位校验和:占2字节,发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分
- 16位紧急指针:占2字节,紧急指针只有在URG = 1时才有意义,实际是一段偏移量,指出紧急数据的末尾在报文段的位置
1.4 TCP主要特点
- TCP是面向连接的运输层协议
- 每一条TCP连接只能由两个端点,每一条TCP连接只能是点对点的
- TCP提供可靠交付的服务,通过TCP连接传送的数据,无差错,不丢失,不重复,并且按序到达
- 面向字节流
2.UDP基础知识
2.1 UDP是什么?
- UDP叫做用户数据报协议,UDP在传送数据前不需要建立连接,UDP不提供复杂的机制,利用IP提供面向无连接的服务
2.2 UDP的协议段格式
- 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程
- 包长度:保存了UDP的首部长度和数据长度的和
- 校验和:校验和是为了提供可靠的UDP首部和数据,检测数据报在传输中是否有错,有错就丢弃
2.3 UDP的主要特点
- UDP是无连接的:知道对端的IP和端口号就直接进行传输, 不需要建立连接
- UDP使用尽最大努力交付(不可靠):没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息
- UDP是面向数据报:不能够灵活的控制读写数据的次数和数量
- UDP没有拥塞控制(直播,视频会议等实时应用)
- UDP支持一对一,一对多,多对一,多对多的交互通信(腾讯早期使用的就是UDP)
- UDP的首部开销小,只有8字节
2.4 UDP的缓冲区
- UDP没有真正意义上的 发送缓冲区. 调用
sendto
会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作 - UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃
- UDP的socket既能读也能写,全双工通信
3.TCP和UDP区别总结
- 连接
- TCP 是面向连接的传输层协议,传输数据前先要建立连接
- UDP是不需要连接,立刻传输数据
- 服务对象
- TCP是一对一的两点服务,一条连接只有两个端点
- UDP支持支持一对一,一对多,多对一,多对多
- 可靠性
- TCP是提供可靠交付数据的,数据可以无差错,不丢失,不重复,按序到达
- UDP是尽最大努力交付,不保证可靠交付数据
- 拥塞控制,流量控制
- TCP有拥塞控制和流量控制,保证数据传输的安全性
- UDP没有,即使网络拥堵,也不影响UDP的传输效率
- 首部开销
- TCP首部20字节
- UDP的首部开销小,只有8字节
- 传输方式
- TCP是流式传输,没有数据边界,保证顺序和可靠
- UDP是一个包一个包发送,有边界,可能会丢包或者乱序
- 应用场景
- TCP:FTP文件传输,HTTP/HTTPS等
- UDP:直播,视频会议,广播通信等
(这个面试可能会问)
4.TCP保证可靠的机制
- TCP保证可靠是因为它拥有很多机制来保证的
4.1重传机制
4.1.1超时重传
- 在TCP的协议段格式中有序列号和确认序列号,通过序列号和确认序列号确认应答
- 当发送端发送的数据到达接收端,接收端会返回一个确认应答消息,表示接受到消息
- 但是网络中的线路有可能导致传输过程中出现各种问题,比如数据丢失,TCP就引入了重传机制来解决这个问题
- 超时重传:在发送数据时,设计一个计时器,如果在规定时间内未返回ACK响应,则重发数据
- 那么如何确定超时的时间呢?
4.1.2快速重传
-
TCP还有一种快速重传机制,不以时间为驱动,而是以数据驱动重传
-
快速重传机制解决了超时时间的问题,但是依然存在一个问题,重传的时候,是重传之前的一个,还是重传所有?
4.1.3 SACK方法
- 在上面的例子中,是重传Seq2,还是Seq3,Seq4,Seq5?
- 发送端并不清楚这连续的三个ACK2都是谁传回来的
- 为了解决应该重传哪些TCP报文,引入
SACK
方法 - SACK(Selective Acknowledgement)选择性确认:这种方式需要在TCP的头部选项字段里加一个SACK的东西,可以将缓存的数据发出,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据
4.1.4 D-SACK
- Duplicate SACK又称 D-SACK:主要使用了D-SACK来告诉发送方有哪些数据被重复接受了
- ACK丢失
- 网络延时
4.2滑动窗口
4.2.1窗口概念引入
- TCP是每发送一个数据,都要进行一次确认应答,当上一个数据包应答之后,再发送下一个
- 但是这样性能较差,尤其是数据往返的时间较长的时候
- 就像两个人说话的时候,我一句你一句,这样效率太低,如果我说完一句话,你在做别的事情,没有回复我,那我不可能干等着你昨晚其他事情,所以就存在一个缺点:数据包的往返时间越长,通信的效率就越低,所以TCP引入
窗口
的概念,即使往返时间长,也不会降低效率 - 窗口的大小:就是不需要等待确认应答,可以继续发送数据的最大值,实质是在操作系统中开辟的一个缓冲区,发送端在等待确认应答的返回之前,必须在缓冲区保留已发送的数据,如果正常收到确认应答,数据就从缓冲区清除
- 举了例子:假设窗口大小是3个TCP段
4.2.2窗口的大小
-
在TCP头部中有一个16位窗口大小字段,接收端用来告诉发送端自己还有多少缓冲区可以接受数据,所以发送端就可以根据这个接收端的能力来发送数据,不会导致接收端处理不过来,所以窗口大小一般都是由接收方决定的
-
发送方发送的数据大小 <= 接收方的窗口大小,否则接收方无法正常接收数据
4.2.3发送方的滑动窗口
-
初始状态
-
可用窗口为0
-
收到确认的状态
4.2.4接收方的滑动窗口
4.3流量控制
- 接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应
- 因此TCP提供一种机制可以让发送方根据接收方的实际接受能力控制发送的数据量,这就叫做流量控制
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端
窗口大小字段越大, 说明网络的吞吐量越高
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端
发送端接受到这个窗口之后, 就会减慢自己的发送速度
如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数 据段, 使接收端把窗口大小告诉发送端
4.3拥塞控制
4.3.1拥塞控制概念引入
- 流量控制是为了避免发送方的数据填满接收方的缓存,但是并不知道网络中发生了什么
- 在网络出现拥堵时,如果继续发送大量的数据报包,可能就会导致数据报时延或丢失,这时就会重传数据,但是一旦重传,就会增大网络的负担,于是会导致更大的延迟和更多的丢包,就形成了一个恶心循环
- 所以在网络发送拥堵时,TCP会降低发送 的数据量,于是就有了
拥塞控制
,控制的目的就是避免发送方的数据填满整个网络
,为了在发送方调节要发送数据的量,引入一个拥塞窗口的概念
4.3.2拥塞窗口
- 拥塞窗口
cwnd
是发送方维护的一个状态变量,他会根据网络的拥塞程度动态变化,发送窗口swnd
,接收窗口rwnd
,这三者的关系为swnd = min(cwnd, rwnd)
,也就是发送窗口是拥塞窗口和接收窗口中的最小值 - 拥塞窗口变化的规则:网络中出现拥塞,拥塞窗口就会减少,反之增大,如果发送方没有在规定时间内收到ACK应答报文,就会认为网络中出现了拥塞
4.3.3拥塞控制的算法
4.3.3.1 慢启动(指数性的增长)
- TCP在刚建立连接完成后,现有一个慢启动过程,这个慢启动的意思就是逐步提高发送数据包的数量,如果一上来一发送大量数据包,容易造成网络拥塞
- TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据,每当发送方收到一个ACK,拥塞窗口就加1
- 慢启动是一种发送端在未检测到拥塞时所采用的积极的避免拥塞的方法,主要思想就是:
TCP模块刚开始发送数据并不知道网络中的实际情况,所以用一种试探的方式平滑增加 cwnd的大小,慢启动如果不加以控制的话,会使得 cwnd快速增长,导致网络拥塞
- 那么慢启动涨到什么时候会停止呢—>
在TCP控制中定义了一个状态量:慢启动阈值(ssthresh),当 cwnd < ssthresh 时,使用慢启动算法,如果 cwnd > ssthresh ,使用拥塞避免算法
4.3.3.2 拥塞避免(线性增长)
- 当拥塞窗口超过慢启动阈值就会进入拥塞避免,一般来说
ssthresh的大小为65535字节
,每当收到一个ACK时, cwnd增加 1/cwnd
- 总结:拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是在增长,但是相对缓慢,一直增长,网络进入拥塞,就会出现丢包,这时就进行超时重传,触发重传,就进入拥塞发生
4.3.3.3 拥塞发生
-
网络出现拥塞,发生重传:超时重传或者快速重传
-
超时重传的拥塞发生算法:ssthresh变为cwnd/2, cwnd重置为1
-
快速重传的拥塞发生算法:cwnd = cwnd/2,ssthresh = cwnd ,进入快速恢复
4.3.3.4 快速恢复
- 快速重传和快速恢复一般同时使用,cwnd = cwnd/2,ssthresh = cwnd 已经更新
- 快速恢复
参考:《计算机网络-谢希仁版》 &《图解网络》
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141669.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...