TCP
TCP
MTU: Maxitum Transmission Unit 最大传输单元 MSS: Maxitum Segment Size 最大分段大小 由于以太网EthernetII最大的数据帧是1518Bytes
标志位:
SYN:建立链接
FIN:中止链接
ACK:确认32位确认序号有效。
注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。
RST:“复位”报文
- 目的端口无监听。当连接请求到达时,目的端口没有进程正在监听,TCP会产生一个复位报文。(在UDP中,则产生一个ICMP端口不可达的信息)
- 异常终止链接。可以通过发送个复位报文段而不是FIN来中途释放一个连接,这种行为称为异常释放( abortive release)。
- 如窗口探测3次都发现接受窗口为0
- 连接中,收到错误的标志信号,如SYN等
- 客户端的连接被关闭,客户的内核就会回 RST 报文,服务端收到后就会释放连接。
URG:
PSH:
有点过时
连接管理
建立连接 - 三次握手
为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?
主要原因有两个方面:
- 为了防止历史报文被下一个相同四元组的连接接收(主要方面);
- 为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收;
TCP 半连接和全连接队列
在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
- 半连接队列,也称 SYN 队列;
- 全连接队列,也称 accept 队列;
不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,默认情况都会丢弃报文。
SYN 攻击
SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满,这样当 TCP 半连接队列满了,后续再在收到 SYN 报文就会丢弃,导致客户端无法和服务端建立连接。
避免 SYN 攻击方式,可以有以下四种方法:
- 调大 netdev_max_backlog;
- 增大 TCP 半连接队列;
- 开启 tcp_syncookies;
- 减少 SYN+ACK 重传次数
tcp_syncookies
具体过程:
- 当 「 SYN 队列」满之后,后续服务端收到 SYN 包,不会丢弃,而是根据算法,计算出一个
cookie
值; - 将 cookie 值放到第二次握手报文的「序列号」里,然后服务端回第二次握手给客户端;
- 服务端接收到客户端的应答报文时,服务端会检查这个 ACK 包的合法性。如果合法,将该连接对象放入到「 Accept 队列」。
- 最后应用程序通过调用
accpet()
接口,从「 Accept 队列」取出的连接。
可以看到,当开启了 tcp_syncookies 了,即使受到 SYN 攻击而导致 SYN 队列满时,也能保证正常的连接成功建立。
net.ipv4.tcp_syncookies 参数主要有以下三个值:
- 0 值,表示关闭该功能;
- 1 值,表示仅当 SYN 半连接队列放不下时,再启用它;
- 2 值,表示无条件开启功能;
那么在应对 SYN 攻击时,只需要设置为 1 即可。
断开连接 - 四次挥手
重传机制
- 超时重传
- 快速重传
- SACK方法
- D-SACK方法
滑动窗口
为解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。
那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。
图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。
流量控制
RcvWindow=RcvBuffer-[LastByteRcvd-LastByteRead]
拥塞控制
- 慢启动
- 拥塞避免
- 拥塞发生
- 快速恢复