可靠数据传输协议
机制 | 说明 |
---|---|
检验和 | 检测在一个传输分组中的比特错误 |
定时器 | 超时重传,接收方可能会收到一个分组的多个冗余副本 |
序号 | 空隙可使接收方检测出丢失的分组。相同序号的分组可使接收方检测出冗余副本 |
确认 | 确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,取决于协议 |
否定确认 | 否定确认报文通常携带着未被正确接收的分组的序号 |
窗口、流水线 | 发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上得到增加。窗口长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置 |
TCP
TCP 基本认识
- 面向连接:只能一对一,不能像UDP的一对多
- 可靠传输:无论网络链路变化,保证报文一定从A的运输层到顶B的运输层(无差错、不丢失、不重复、按序到达)
- 基于字节流:消息可能被分组,接收端需要知道消息的边界,以读出有效信息;报文有序
TCP四元组唯一确定一个连接:(源地址,源端口,目的地址,目的端口)
TCP报文
- 序号:解决包的乱序
- 确认序列:解决丢包
- 窗口大小:流量控制、拥塞控制的缓存大小
- 序列号=上一次发送的序列号+len(数据长度)
如果上一次发送的报文是SYN报文或者FIN报文,则改为上一次发送的序列号+1 - 确认号=上一次收到的报文中的序列号+len(数据长度)
如果收到的是SYN报文或者FIN报文,则改为上一次收到的报文中的序列号+1
状态位
- SYN(Synchronize) :发起连接
- ACK(Acknowledgment):确认应答
- RST:重新连接
- FIN:结束连接
- URG:指示紧急数据的存在,通常与紧急指针字段一起使用
- PSH:指示数据的即时传输和处理
队头阻塞
若HTTP请求信息超过MSS长度,TCP将其拆解为块,IP包自然就长度不大于MTU
否则丢失IP分片,导致需要重传整个IP报文的所有分片
- MTU(Maximum Transmission Unit)(最大传输单元):一个网络包的最大长度
- MSS(Maximum Segment Size)(最大分段大小):除去IP和TCP头部后,一个网络包容纳TCP数据的最大长度
TCP三次握手
三次握手
- 保证双方都有发送和接收的能力
- 第三次握手可以携带数据
- 避免历史连接(通过RST报文)
- 同步双方初始序列号
- 避免资源浪费,若两次握手,服务器可能白白建立连接
报文丢失
超时重传
ACK报文不会重传,ACK丢失时,由对方重传对应的报文
SYN攻击
- 半连接队列:SYN队列,哈希表
- 全连接队列:accept队列,链表
SYN攻击占满半连接队列(SYN队列),后续再收到SYN报文会丢弃
SYN cookies技术
过程
-
当服务器收到一个SYN请求时,它会生成一个加密的cookie,其中包含了一些关键的连接信息,如源IP地址、端口号等。
-
服务器将生成的cookie作为序列号的一部分,将SYN+ACK响应发送给客户端。响应中的序列号包含了加密的cookie,以便客户端在后续的ACK确认中提供该cookie进行验证。
-
客户端在收到服务器的SYN+ACK响应时,会解析响应中的序列号,并提取出加密的cookie。
-
当客户端发送ACK确认时,它会将之前接收到的cookie作为有效凭证一起发送给服务器。
-
服务器在接收到ACK确认时,会对接收到的cookie进行解密和验证。如果验证通过,服务器会根据cookie中的信息重建连接状态,完成TCP连接的建立。
缺陷
- 服务端不会保存连接信息,丢包无法重传
- 耗CPU,可能导致ACK攻击(构造第三次握手ACK包携带瞎编的cookies信息,耗尽CPU)
快速建立连接
TCP四次挥手
四次挥手
客户端发送FIN,表示客户端不再发送数据,但可以接收数据
挥手丢失
第四次挥手丢失
2MSL(Maximum Segment Lifetime)
默认一共是60s,为了至少允许报文丢失一次(丢失的ACK+重传的FIN)
TIME_WAIT状态
- 时间足以使得两个方向原来的数据包自然消失,避免收到残留报文导致数据错乱
- 等待足够时间,确保被动方接收到最后的ACK,从而正常关闭
特殊:三次挥手
条件:被动关闭方没有数据要发送、开启了TCP延迟确认机制
延迟确认(见下面的“糊涂窗口”)
- 当有响应数据要发送时,ACK会随看响应数据一起立刻发送给对方
- 当没有响应数据要发送时,ACK将会延迟一段时间,以等待是否有响应数据可以一起发送
- 如果在延退等待发送ACK期间,对方的第二个数据报文又到达了,这时就会立刻发送ACK
关闭连接的函数
- close函数,同时socket关闭发送方向和读取方向,也就是socket不再有发送和接收数据的能力。如果有多进程/多线程共享同一个socket,如果有一个进程调用了close关闭只是让socket引用计数-1,并不会导致socket不可用,同时也不会发出FIN报文,其他进程还是可以正常读写该socket,直到引用计数变为0,才会发出FIN报文。
- shutdown函数,可以指定socket只关闭发送方向而不关闭读取方向,也就是socket不再有发送数据的能力,但是还是具有接收数据的能力。如果有多进程/多线程共享同一个socket,shutdown则不管引用计数,直接使得该socket不可用,然后发出FIN报文如果有别的进程企图使用该socket,将会受到影响。
可靠传输
重传机制
超时重传(定时器)
RTT(Round-Trip Time,往返时延)
RTO(Retransmission Timeout,超时重传时间)应略大于RTT
快速重传(数据驱动)
三次同样的ACK就触发重传
SACK(Selective Acknowledgement)选择性确认
在TCP头部“选项”字段添加SACK,将已收到的数据信息发给发送方,从而只传丢失的数据
D-SACK(Duplicate SACK)
使用SACK告诉发送方哪些数据被重复接收
滑动窗口
流量控制
发送方根据接收方的实际接收能力,控制发送的数据量(避免发送方的数据填满接收方的缓存)
必须先收缩窗口,再减少缓存,否则可用窗口可能为负值,导致丢包
糊涂窗口
让接收方不通告小窗口给发送方;让发送方避免发送小数据
拥塞控制
避免发送方的数据填满网络
发送窗口swnd = min(接收窗口rwnd,拥塞窗口cwnd)
慢启动、拥塞避免
cwnd < 慢启动门限ssthresh 时,使用慢启动算法,否则使用拥塞避免算法
拥塞发生算法
发生超时重传的拥塞时
快速恢复算法
发生快速重传三次ACK的拥塞时,认为拥塞不严重
流水线
TCP Fast Open
TCP的缺陷
- 升级TCP的工作很困难
- TCP建立连接的延退(TCP三次握手、TLS四次握手)
- TCP存在队头阻塞问题
- 网络迁移需要重新建立TCP连接(IP改变导致四元组改变)
Socket编程
UDP
UDP报文
区别
- UDP的头部只有8个字节(64位),TCP首部20个字节(不包括“选项”)
- UDP不需要连接,即刻传输数据
- UDP支持一对一、一对多、多对多
- TCP可靠性
- TCP拥塞控制、流量控制
- TCP流式传输,没有边界,但保证顺序和可靠;UDP按包发送,有边界但可能丢包乱序
- TCP数据大于MSS时,在运输层分片/组装;UDP数据大于MTU时,在网络层分片/组装
QUIC
QUIC处于应用层
可靠传输
Packet Header
- Long Packet Header:首次建立连接
- Short Packet Header:日常传输数据
Packet Number严格递增
- 可以更加精确计算RTT,没有TCP重传的岐义性问题
- 支持乱序确认,解决队头阻塞问题
QUIC Frame Header
一个packet可以存放多个frame
Stream类型
Offset:类似于TCP协议中的Seq序号,保证数据的顺序性和可靠性
丢包重传时,Packet Number严格递增,比较两个数据包的StreamID与StreamOffset,若都一致,就说明这两个数据包的内容一致,为重传的数据包
队头阻塞的解决
QUIC给每一个Stream都分配了一个独立的滑动窗口
流量控制
- Stream级别的流量控制
Stream可以认为就是一条HTTP请求,每个Stream都有独立的滑动窗口,所以每个Stream都可以做流量控制,防止单个Stream消耗连接(Connection)的全部接收缓冲
- Connection流量控制
限制连接中所有Stream相加起来的总字节数,防止发送方超过连接的缓冲容量
更快的连接建立
参见HTTP3,QUIC内部包含TLS1.3
连接迁移
QUIC协议没有用四元组的方式来“绑定”连接,而是通过连接ID来标记通信的两个端点,客户端和服务器可以各自选择一组ID来标记自己。因此即使移动设备的网络变化后,导致IP地址变化了,只要仍保有上下文信息(比如连接ID、TLS密钥等),就可以“无缝”地复用原连接