因特网协议概述
常用协议 | |
---|---|
应用层 | HTTP(超文本传输协议)、FTP(文件传输协议)、SMTP(简单邮件传输协议)、DNS(域名系统)、DHCP(动态主机配置协议)、SNMP(简单网络管理协议) |
运输层 | TCP(传输控制协议)、UDP(用户数据报协议)、SCTP(流控制传输协议)、DCCP(数据报式传输协议) |
网络层 | IP(Internet Protocol) 、ICMP(Internet Control Message Protocol)、ARP(地址解析协议)、RAR(逆地址解析协议)、OSPF(开放最短路径优先)、BGP(边界网关协议) |
链路层 | PPP(点对点协议)、HDLC(高级数据链路控制)、Ethernet(以太网)、Wi-Fi(无线局域网)、ATM(异步传输模式)、FDDI(光纤分布式数据接口) |
物理层 | IEEE 802.3(以太网)、IEEE 802.11(Wi-Fi)、SONET(同步光纤网络)、DSL(数字用户线路)、USB(通用串行总线)、HDMI(高清晰度多媒体接口) |
可靠数据传输协议
机制 | 说明 |
---|---|
检验和 | 检测在一个传输分组中的比特错误 |
定时器 | 超时重传,接收方可能会收到一个分组的多个冗余副本 |
序号 | 空隙可使接收方检测出丢失的分组。相同序号的分组可使接收方检测出冗余副本 |
确认 | 确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,取决于协议 |
否定确认 | 否定确认报文通常携带着未被正确接收的分组的序号 |
窗口、流水线 | 发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上得到增加。窗口长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置 |
TCP
- 面向连接:只能一对一,不能像UDP的一对多
- 可靠传输:无论网络链路变化,保证报文一定从A的运输层到顶B的运输层(无差错、不丢失、不重复、按序到达)
- 基于字节流:消息被分组,接收端需要知道消息的边界,以读出有效信息;报文有序
连接:用于保证可靠性和流量控制维护的某些状态信息的组合,包括Socket、序列号和窗口大小
TCP四元组唯一确定一个连接:(源地址,源端口,目的地址,目的端口)
TCP & UDP
TCP报文
- 序号:解决包的乱序
- 确认序列:解决丢包
- 窗口大小:流量控制、拥塞控制的缓存大小
- 序列号=上一次发送的序列号+len(数据长度)
如果上一次发送的报文是SYN报文或者FIN报文,则改为上一次发送的序列号+1 - 确认号=上一次收到的报文中的序列号+len(数据长度)
如果收到的是SYN报文或者FIN报文,则改为上一次收到的报文中的序列号+1
状态位
- SYN(Synchronize) :发起连接
- ACK(Acknowledgment):确认应答
- RST:重新连接
- FIN:结束连接
- URG:指示紧急数据的存在,通常与紧急指针字段一起使用
- PSH:指示数据的即时传输和处理
队头阻塞
UDP报文
区别
- UDP的头部只有8个字节(64位),TCP首部20个字节(不包括“选项”)
- UDP不需要连接,即刻传输数据
- UDP不仅支持一对一
- TCP可靠性
- TCP拥塞控制、流量控制,保证数据的安全性
- TCP面向字节流,没有边界,保证顺序和可靠;UDP面向报文
- TCP数据大于MSS时,在运输层分片/组装;UDP数据大于MTU时,在网络层分片/组装
- MTU(Maximum Transmission Unit)(最大传输单元):一个网络包的最大长度
- MSS(Maximum Segment Size)(最大分段大小):除去IP和TCP头部后,一个网络包容纳TCP数据的最大长度
TCP三次握手
三次握手
- 保证双方都有发送和接收的能力
- 第三次握手可以携带信息
- 避免历史连接
- 同步双方初始序列号
客户端和服务端的初始化序列号都是随机生成,能很大程度上避免历史报文被下一个相同四元组的连接接收,然后又引入时间截的机制,从而完全避免了历史报文被接收的问题
时间戳
TCP协议中的时间戳选项(Timestamp Option)用于在TCP报文段中携带时间戳信息。时间戳机制旨在提供更精确的计时和延迟测量,以及解决一些与序列号回绕相关的问题。
时间戳选项由TCP报文段的TCP头部中的"Options"字段中的一个选项来表示。时间戳选项包含以下字段:
- Kind(1字节):指示选项类型为时间戳选项,其值为8。
- Length(1字节):指示选项长度,通常为10字节(包括Kind和Length字段)。
- TSval(4字节):发送端记录的时间戳值,表示发送报文段的发送时间。
- TSecr(4字节):接收端对应的时间戳值,用于对应发送端的时间戳。
TCP的时间戳机制具有以下作用和特点:
- 延迟测量:接收端可以使用时间戳信息来测量从发送报文段到接收报文段之间的往返时间(RTT)和延迟。
- 解决序列号回绕问题:TCP序列号是一个32位无符号整数,当序列号达到最大值后会回绕到0。时间戳机制可以帮助区分新旧报文段,避免由于序列号回绕导致的混淆。
- 连接验证:时间戳信息可以用于验证连接的合法性和活跃性,确保报文段的来源是可信的。
- 同步时钟:时间戳机制可以用于同步发送端和接收端的时钟,提供更准确的时间基准。
如果发现收到的数据包中时间截不是递增的,则表示该数据包过期,就会直接丢弃这个数据包
客户端A和客户端B因为经过相同的NAT网关服务端建立TCP连接,所以是用相同的IP地址与服务端建立TCP连接。如果客户端B的timestamp比客户端A的timestamp小,那么由于服务端的per-host的PAWS机制(同时开启net.ipv4.tcp_timestamps和net.ipv4.tcp_tw_recycle时触发),服务端就会丢弃客户端主机B发来的SYN包
握手丢失
握手丢失,导致超时重传,每次等待的RTO(Retransmission Timeout,超时重传时间)翻倍
查看最大重传次数:
cat /proc/sys/net/ipv4/tcp_syn_retries
cat /proc/sys/net/ipv4/tcp_synack_retries
第一次握手丢失
若第二、三次握手丢失,ACK报文不会重传,而是由对方重传
第二次握手丢失
第三次握手丢失
SYN攻击
服务端每收到一个SYN,就进入SYN_RCVD状态,但发出的ACK+SYN报文无法得到ACK应答,进而占满服务端的半连接队列(SYN队列),后续再收到SYN报文会丢弃
半连接队列等待第三次握手,取出对应的IP端口的连接
- 半连接队列:SYN队列,哈希表
- 全连接队列:accept队列,链表
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四次挥手
四次挥手
特殊:三次挥手
条件:被动关闭方没有数据要发送、开启了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,将会受到影响。
close函数(粗暴)
shutdown函数
挥手丢失
第一次挥手丢失
第二次挥手丢失
第三次挥手丢失
FIN_WAIT2的最大时长默认为60秒
第四次挥手丢失
TIME_WAIT=2MSL
- 至少允许报文丢失一次(丢失的ACK+重传的FIN)
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收(时间足以使得两个方向原来的数据包自然消失)
- 确保被动方接收到最后的ACK,从而正常关闭
可靠传输
重传机制
超时重传(定时器)
RTT(Round-Trip Time,往返时延)
RTO应略大于RTT
快速重传(数据驱动)
SACK选择性确认
Selective Acknowledgement
在TCP头部“选项”字段添加SACK,将已收到的数据信息发给发送方,从而只传丢失的数据
D-SACK(Duplicate SACK)
- 可以让发送方知道,是发出去的包丢了,还是接收方回应的ACK包丢了;
- 可以知道是不是发送方的数据包被网络延时了;
- 可以知道网络中是不是把发送方的数据包给复制了
滑动窗口
窗口大小
无需等待确认应答,而可以继续发送数据的最大值;实际是缓存空间
累计应答/累计确认
滑动窗口指针
接收窗口大小约等于发送窗口
发送窗口
发送方收到ACK确认后,可用窗口增加,即窗口滑动(否则可能导致队头阻塞)
接收窗口
收到有序数据时,窗口滑动(否则可能导致队头阻塞)
流量控制(窗口)
发送方根据接收方的实际接收能力,控制发送的数据量(避免发送方的数据填满接收方的缓存)
必须先收缩窗口,再减少缓存,否则可用窗口可能为负值,导致丢包
窗口关闭
糊涂窗口
让接收方不通告小窗口给发送方;让发送方避免发送小数据
Nagle算法
延迟确认
- 当有响应数据要发送时,ACK会随看响应数据一起立刻发送给对方
- 当没有响应数据要发送时,ACK将会延迟一段时间,以等待是否有响应数据可以一起发送
- 如果在延退等待发送ACK期间,对方的第二个数据报文又到达了,这时就会立刻发送ACK
拥塞控制
避免发送方的数据填满网络
发送窗口swnd = min(接收窗口rwnd,拥塞窗口cwnd)
慢启动算法
逐渐提高发送数据包的数量
发送方每收到一个ACK,cwnd就加1
cwnd < 慢启动门限ssthresh 时,使用慢启动算法,否则使用“拥塞避免算法”
拥塞避免算法
发送方每收到一个ACK,cwnd就加1/cwnd
后半段由指数增长变为线性增长
拥塞发生算法
超时重传
- ssthresh设为cwnd/2
- cwnd初始化(图中假设为1)
查看cwnd的初始化值
快速恢复算法
快速重传
- cwnd = cwnd/2
- ssthresh = cwnd
流水线
TCP的缺陷
- 升级TCP的工作很困难
- TCP建立连接的延退
- TCP存在队头阻塞问题
- 网络迁移需要重新建立TCP连接
UDP
UDP报文
QUIC
Packet Header
- Long Packet Header:首次建立连接
- Short Packet Header:日常传输数据
Packet Number严格递增
- 可以更加精确计算RTT,没有TCP重传的岐义性问题
- 支持乱序确认,解决队头阻塞问题
QUIC Frame Header
Stream类型
Offset:类似于TCP协议中的Seq序号,保证数据的顺序性和可靠性
丢包重传时,Packet Number严格递增,比较两个数据包的StreamID与StreamOffset,若一致,就说明这两个数据包的内容一致,为重传的数据包
队头阻塞的解决
- TCP的发送窗口收到ACK确认时窗口滑动
- TCP的接收窗口收到有序数据时窗口滑动
- HTTP2多个stream复用一个TCP连接
QUIC给每一个Stream都分配了一个独立的滑动窗口
流量控制
Stream级别的流量控制
Stream可以认为就是一条HTTP请求,每个Stream都有独立的滑动窗口,所以每个Stream都可以做流量控制,防止单个Stream消耗连接(Connection)的全部接收缓冲
接收窗口的左边界取决于接收到的最大偏移字节数
当图中的绿色部分数据超过最大接收窗口的一半后,最大接收窗口向右移动,接收窗口的右边界也向右扩展,同时给对端发送窗口更新顿,当发送方收到接收方的窗口更新顿后发送窗口的右边界也会往右扩展,以此达到窗口滑动的效果
Connection流量控制
限制连接中所有Stream相加起来的总字节数,防止发送方超过连接的缓冲容量
更快的连接建立
QUIC内部包含TLS1.3
连接迁移
QUIC协议没有用四元组的方式来“绑定”连接,而是通过连接ID来标记通信的两个端点,客户端和服务器可以各自选择一组ID来标记自己。因此即使移动设备的网络变化后,导致IP地址变化了,只要仍保有上下文信息(比如连接ID、TLS密钥等),就可以“无缝”地复用原连接