TCP&UDP
目录
TCP/UDP
传输层
传输层的定义
- 传输层是第一次在端对端也就是主机对主机的一层 专门负责处理上层的数据 负责可靠不可靠的传输
- 传输层下层是使用 mac 和 ip 地址来寻找到特定主机 但是光凭 mac ip 可不够,还要需要端口, 也是在这一层 有了端口的概念
- 这一层的数据单位称为数据段
TCP
TCP 概念 & 流控制
- TCP是 面向有链接 协议, 因为他是 流控制,也被称作流式协议
- 流控制: 接收端告诉发送端 我这边可以接收多少数据 发送端也跟接收端确认少数据 取最小值 将数据切割
- TCP是可靠协议 他有一系列的方法来实现可靠性
TCP 序列号 & 应答机制
- 引出:
- TCP是一个可靠协议 所以TCP需要各种方法来保证他的可靠性
- 解决:
- 发送端发送数据后 接收端接收成功 会发送一个ACK应答
- 缺点
- 万一ACK发送失败 发送端就会一直发送数据 这是一个问题
- 优化:
- 引入 序列号 机制 为每一个数据都打上一个序列号 ACK也算一个序列号
- 接收端收到数据后, 就会分析她下一次应该接收数据的序列号 然后以ACK应答出去
TCP 重发超时
-
RTT 与 RTO
-
RTT 和 RTO的概念
- TCP作为一个面向有连接型的协议,需要保证其可靠性 所以其内部实现了一个重传计时器
- 每发送一个数据包,就给这个数据设置一个重传计时器, 如果在计时器超时之前收到了针对这个数据包的ack,就取消这个计时器。如果没有收到,则开始发起重传。计时器超时的时间被称为RTO,这个时间的确定取决于RTT
-
-
关于两者详细的解释
RTT(Round Trip Time): 一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值RTO(Retransmission Time Out): 重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传
-
RTT的测量
- 每发送一个分组 TCP就会对其采样,这个采样只会对同一时间的数据段进行采样, 随后用这个采样记录当作RTT
- 采样方法一般有两种
- 采用时间戳: 发送一个数据段时在TCP选项中记录下时间 收到数据段再记录下时间 随后计算出差值 就把这个差值作为RTT 但是需要发送端和接收端都需要支持这个选项
- 重传队列中数据包的TCP控制块: 每个数据包第一次发送出去后都会放到重传队列中,数据包中的TCP控制块包含着一个变量,tcp_skb_cb->when,记录了该数据包的第一次发送时间。如果没有时间戳选项,那么RTT就等于当前时间和when的差值
-
RTO的计算
- 为了避免单次RTT波动,计算RTO时新引入了变量SRTT,表示更加平滑的RTT数值,它的计算方法:
1 2SRTT = x(SRTT) + (1 - x)RTT; // x被称为平滑因子,一般建议设置在[0.8, 0.9],意思是SRTT值百分之八十来自于之前的值,百分之二十来自于当前值。- 计算RTO的方法为:
1 2RTO = min(ubound, max(lbound, y(SRTT))); // y 是时延离散因子,推荐值为[1.3, 2.0],ubound是RTO的上边界,lbound是RTO的下边界 -
算法的缺点
- 在RTT波动较大时,RTO不能明显适应网络变化
-
标准方法
- 标准方法引入了平均偏差的概念,它类似于统计学里面的方差,但是因为方差的计算过程代价较大,对于快速TCP来说不太适合。假设rtt的值为M,RTO的计算方式为:
1 2 3 4 5 6 7srtt = (1 - g)srtt + g(M); rttval = (1 - h)rttval + h(|M - rttval|); RTO = srtt + 4(rttval); /* 其中`g`设置为1/8,`h`设置为`1/4` 对srtt而言,它有1/8取决于当前值,7/8取决于现有值 当RTT变化时,偏差增量越大,RTO的增量也越大 */
TCP 三次握手
- 第一次握手
- SYN = 1,seq = x
- 客户端发送一个 SYN标志为1,指明客户端打算连接的服务器端口,以及初始的序列号为随机生成的 x 的一个TCP包
- 发送完成后,客户端进入
SYN_SEND状态
- 第二次握手
- SYN = 1,ACK = 1,seq = y,ACKNum = x+1
- 服务端发回 SYN=1 ACK=1,并且ACK也算一个序列号+1 所以ACKNum是 x+1,随后再把自己要发送的数据标上序列号,序列号变成了 y 的一个TCP包
- 服务端发送完毕后 进入
SYN_RECV状态
- 第三次握手
- ACK = 1,ACKNum = y+1
- 客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
- 发送完毕 客户端进入
ESTABLISH状态,服务端接收到这个包后 也进入ESTABLISH状态,TCP握手结束
TCP 四次挥手
- 第一次挥手
- FIN = 1, seq = x
- 假设客户端想要关闭连接,客户端发送一个FIN标志位置为1的包,表示自己像断开连接 没有数据可以发送了,但是仍然可以接收数据
- 发送完毕后,客户端进入
FIN_WAIT_1状态
- 第二次挥手
- ACK = 1 ACKNum = x + 1
- 服务端接受到了客户端想要断开的链接的请求,便发出了一个ACK标志位为1的TCP包,表明自己接收到了请求,但还没有准备好关闭连接
- 发送完毕后,服务器端进入
CLOSE_WAIT状态,客户端接收到了这个确认包之后,进入FIN_WAIT_2的状态,等待服务端关闭连接
- 第三次挥手
- FIN=1,seq=y
- 服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1
- 发送完毕后,服务器端进入
LAST_ACK状态,等待来自客户端的最后一个ACK
- 第四次挥手
- ACK = 1,ACKNum = y + 1
- 客户端接收到来自服务器端的关闭请求,发送一个确认包
- 客户端发送完包并进入
TIME_WAIT状态,等待可能出现的要求重传的ACK包 - 服务端接收到这个确认包后,关闭连接,进入
CLOSED状态
为什么需要3次握手 挥手需要4次
- 3次握手目的是要确定客户端和服务端通信也就是两边的“收”“发”功能是否正常
- 第一次握手: 客户端发送网络包 服务器收到了 这个时候服务端就能确定客户端的"发" 和 服务端的 “收” 都没有问题
- 第二次握手: 服务端发送 这个时候客户端就能确定 客户端的 “发收” 服务端的 “收发” 是没有问题的 但是服务端还没有确定
- 第三次握手: 客户端发包 这个时候服务端就能确定 服务端的 “发收” 客户端的 “收发” 是没有的问题的
- 所以综上所述 两次握手是没有办法确定 双方的通信能力是否正常 至少也要三次
- 4次挥手目的是要数据接收完毕 不能在数据没有接受完全就直接关闭了连接TCP连接是双向传输的对等的模式, 就是说双方都可以同时向对方发送或接受数据
- 第一次挥手: 当有一方要关闭连接时,会发送指令告知对方 我们要close连接
- 第二次挥手: 对方回一个ack应答 此时一个方向的连接关闭 但是!另一个方向仍然可以继续传输数据 等到数据发送完 就进行第三次挥手
- 第三次挥手: 发送FIN段
- 第四次挥手: 回ACK应答
TCP 以段为单位发送数据
- 什么是段
- 在三次握手的期间发送端会告诉接受端 我一次性能发送多少数据接受端也会告诉发送端 我一次性能接受多少数据随后发送端就会把全部数据切割为最小值 发送出去
- 这个最小值为段
TCP 窗口控制
- 引出
- 当我们以段为单位 一次发送数据 随后又得等待ack 这样包的往返效率很慢
- 所以tcp采用了滑动窗口来优化
- 什么是窗口控制
- 滑动窗口就是我们设置窗口值 在没超够窗口值的前提下 不用等待ack的应答 就可以发送段数据
TCP 重发控制
- 引出
- 当我们利用窗口来发送数据 如果有数据丢失怎么办
- ACK丢失
- 如有几个ack应答丢失是没有关系的 因为剩下的ack会到达发送端
- 所以一对比单个段发送的效率 窗口的性能还是蛮高的
- 段丢失
- 如果段数据丢失了 接受端会一直返回相对应的序列号的ack应答 如果发送端一直接受同样的ack三次 就会进行重发
TCP 拥塞窗口
-
引出
- 计算机网络都处在一个共享的环境。 因此也有可能会因为其他主机之间的通信会造成网络拥堵。如果在网络拥堵的时候 发送一个较大量的数据包 会可能导致整个网络的瘫痪
-
什么是拥塞窗口
- TCP在通信一开始的时候会通过一个叫做慢启动的算法得出的数值,对发送数据量进行控制
- 首先,为了在发送端调节要发送数据的量,定义了一个叫做 “拥塞窗口” 的概念 在慢启动的时候,将这个窗口调节为1个数据段发送数据,随后没收到一个ack就进行+=1 在发送数据的时候 也会将其拥塞窗口的大小和接受端的窗口对比 取其最小值 然后发送比这个最小值还小一点的数据
-
上述解决办法会出现的问题
- 随着包的往返,拥塞窗口也会以 1 2 4 等指数函数增长,拥堵状况激增甚至导致拥塞的发生
- 慢启动阈值他也就来了
-
慢启动阈值
-
只要拥塞窗口的值超过这个范围,则每一次收到ack,只允许下面这种比例放大拥塞窗口
-
11个数据段的字节数 / 拥塞窗口(字节) X 1个数据段字节数
-
TCP 连接状态
- CLOSED: 初始状态
- LISTEN: 服务器处于监听状态
- SYN_SEND: 客户端socket执行CONNECT连接,发送syn包 进入此状态
- SYN_RECV: 服务端收到SYN包并发送服务端SYN包,进入此状态
- ESTABLISH: 表示连接建立。客户端发送了最后一个ACK包后进入此状态,服务端接 收到ACK包后进入此状态
- FIN_WAIT_1: 终止连接的一方发送了FIN报文后进入,等待对方FIN
- CLOSE_WAIT: 假设服务器)接收到客户机FIN包之后等待关闭的阶段。在接收到 对方的FIN包之后,自然是需要立即回复ACK包的,表示已经知道断开请求。但是本 方是否立即断开连接(发送FIN包)取决于是否还有数据需要发送给客户端,若有, 则在发送FIN包之前均为此状态
- FIN_WAIT_2: 此时是半连接状态, 既有一方要求关闭连接,等待另一方关闭,客户端接收到服务端的ACK包,但并没有接收到服务端的FIN包,进入FIN_WAIT_2状态
- LAST_ACK: 服务端发动最后的FIN包,等待最后的客户端ACK响应,进入此状态。
- TIME_WAIT: 客户端收到服务端的FIN包,并立即发出ACK包做最后的确认,在此之 后的2MSL时间称为TIME_WAIT状态。
TCP 首部格式
- 源端口号
- 发送方的应用端口 占16位
- 目标端口号
- 接受端的应用端口 占16位
- 序列号
- 占32位
- 发送端会在发送数据的每个字节上标上序号
- 不一定每一个序列号都是0, 1开始的 这个序列号是随机的 会在syn包也就是第一次握手 告诉接受端
- syn包 & fin包 & ACK 都是一个字节
- 确认应答号
- 占32位
- 收到数据后 会返回下一次应该收到的序列号信息作为确认应答
- 这个确认应答也就代表着 前面的数据我都收到了的意思
- 偏移量
- tcp首部长度
- 保留
- 占4位 主要是为了以后扩展用的 一般都是0
- 控制位
- 字段长为8位, 当它们对应位上的值为1 就会有特殊作用
- 三次握手, 四次挥手
- ack 该位为1时, 确认应答的字段变为有效, tcp规定除了最开始的syn包外, 必须为1
- syn 该位为1时, 代表希望建立连接, 并且序列号会随机生成
- fin 该位为1时, 代表希望断开连接
- 英语全称(其实一开始作者看错了 以为都是p开头 结果阴差阳错发现了 然后就记住了 哈哈) =-=
- psh(push) 该位为1时,代表需要直接将数据传给上一层 / 该位为0时 需要先进行缓存在进行传送
- rst(reset) 该位为1时,代表tcp连接出现异常 必须强制断开连接 比如对方突然断电
- 紧急指针相关
- URG 该位为1时,代表包中有需要紧急处理的数据
- 拥塞窗口
- cwr 该位为1时,则会通知对方拥塞窗口缩小
- ece 该位为1时,通知对方,从对方到这里的网络有拥塞
- 窗口大小
- 长为16位
- 代表发送数据最多不能超过这个窗口大小
- 如果窗口大小为0 则就是代表可以窗口探测 去寻找最新的窗口大小
- 校验和
- 紧急指针
- 该字段长为16位, 只有在URG控制位为1时有效
- 数据的首位到紧急指针指向的位置 都是紧急数据
- 怎么处理紧急数据 就是应用层要实现的事情, 一般在暂停通信 或者中断通信的时候使用
- 选项
UDP
UDP 概念 & 用途
- UDP 是面向无连接协议 爆式协议 他不会去管数据到底有没有到达 所以想啥时候发数据就啥时候发数据经常用于广播 视频 音频等
UDP 首部格式
- 因为UDP简单 所以首部只有 源端口号 目标端口号 检验和 包长度 四个数据