1. 协议分层模型

OSI参考模型 TCP/IP参考模型 常见协议

应用层

应用层

HTTP FTP SMTP SSH

表示层

会话层

传输层

传输层

TCP UDP RTP SCTP

网络层

互联网层

IP ICMP

数据链路层

网络接口层

IEEE802.3 IEEE802.11

物理层

Table 1. 数据包格式
以太网首部 IP首部 TCP首部 HTTP请求/响应头 数据

L2

L3

L4

L7

应用程序

<20字节>

<20~60字节>

<MSS>

<MTU: 1500/9000>

2. TCP协议简介

TCP 是一种面向连接的、可靠的、有序的、基于字节流的传输层通信协议. [RFC9293]

  • 面向连接: 确认通信设备间连接的开始和结束, 同时通过拥塞控制机制保障端到端的高可靠通信.

  • 可靠的: 通过ACK确认机制确保数据传输完成.

  • 有序的: 通过SEQ机制对收到的分段顺序进行管理.

  • 基于字节流: 无边界.

3. TCP连接

3.1. 握手流程

3.1.1. 标准三次握手流程

3whs
  • 如果发送SYN包没有收到ACK, 则会经过 \$"RTO"\$ 秒后重传, 如果依然没有收到则会经过 \$2xx"RTO"\$ 秒后重传, 以此类推, 可以通过系统变量 net.ipv4.tcp_syn_retries 设置最大SYN包重传次数, net.ipv4.tcp_synack_retries 设置最大SYN+ACK包重传次数.

  • 当服务端收到SYN包后, 会检查 半连接队列 参数 net.ipv4.tcp_max_syn_backlog/net.core.somaxconn/backlog , 如果半连接数超过了这一阈值, 会拒绝该连接.

  • 响应ACK, 将连接加入到 全连接队列 时, 会检查 全连接队列 参数 net.core.somaxconn/backlog , 如果全连接数超过了这一阈值, 会拒绝该连接.

  • 建立连接后, 经过 (tcp_keepalive_time + net.ipv4.tcp_keepalive_intvl * net.ipv4.tcp_keepalive_probes) 秒后无响应则会关闭连接.

  • 建立后的连接收发包会固定在一个CPU核上进行.

  • 如果半连接队列/全连接队列满了, 会导致第一次握手SYN丢包或者第三次握手ACK"丢包"/收到RST包(设置了 tcp_abort_on_overflow=1), 一直重试. 检查方式:

    • ss -lnt

    • watch 'netstat -s | grep overflowed'

    • ss -antp | grep SYN-RECV | wc -l

3.1.6. TCP FastOpen (RFC 7413)

net.ipv4.tcp_fastopen = 3

tcp-fast-open

3.2. 挥手流程

3.2.4. so_linger

struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));
tcp-so-linger

4. 拥塞控制

4.1. 窗口

  • \$"rwnd"\$ : 发送方从收到的ACK中被告知的接收方缓冲区大小.

  • \$"cwnd"\$ : 发送方经过拥塞控制算法计算出的拥塞窗口大小.

  • \$"swnd"\$ : 发送方最终计算到的发送窗口大小. \$"swnd"=min("rwnd","cwnd")\$

4.2. 基本阶段

4.2.1. 慢启动

  1. 第一次 cwnd = 1 , 后续窗口加上收到的ACK个数.

  2. 当发生丢包 (超时重传或收到重复ACK) 时, cwnd 重置为1, 重新进行慢启动.

4.2.2. 拥塞避免

  1. 设置 ssthresh 阈值为 cwnd 的一半.

  2. cwnd < ssthresh 时, 仍然指数增长 cwnd .

  3. cwnd >= ssthresh 时, 每收到 cwnd 个ACK时, cwnd 才会加1, 实现慢速线性增长.

4.2.3. 快速恢复

  1. 收到重复ACK时, 设置 ssthresh 阈值为 cwnd 的一半, 重置 cwnd = cwnd / 2 + 重复ACK个数. (快速重传)

  2. 收到重传报文对应的ACK后, 重置 cwnd = ssthresh , 进行拥塞避免阶段.

tcp cc
Figure 1. TCP拥塞控制阶段

4.2.4. 拥塞控制算法

  • Reno

  • NewReno

  • CUBIC

  • BBR

5. 工具

5.1. tcpdump

# 监听指定网卡
tcpdump -i eth0

# 监听指定端口
tcpdump port 8080
tcpdump portrange 8000-8100

# 监听指定ip(段)
tcpdump host 192.168.0.2
tcpdump net 192.168.0.0/24

# 监听指定来源/目标ip/端口的报文
tcpdump dst 1.0.0.1
tcpdump -nnvvS src 10.5.2.3 and dst port 3389
tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)'

# 监听RST包
tcpdump 'tcp[13] & 4!=0'tcpdump 'tcp[tcpflags] == tcp-rst'
# 监听SYN和RST包
tcpdump 'tcp[13] = 6'

# 监听GET请求
tcpdump -vvAls0 | grep 'GET'

# 输出结果到指定文件
tcpdump host 192.168.0.2 -w 02.cap

5.2. netstat

# 查看端口占用的进程
sudo netstat -lnp | grep 22| awk '{print $NF}'

# 查看进程使用的端口号:
sudo netstat -atpn | grep <PID>

# 查看tcp使用情况分析
sudo netstat -st

# 查看所有监听的unix socket
sudo netstat -lx

5.3. ss

# 查看指定目标地址/端口的连接
ss dst 192.168.0.2

# 查看指定状态的socket
ss state ESTABLISHED

# 查看port小于1024的socket
ss -n sport \< 1024

# 查看tcp使用情况分析
ss -s