文章目录
- [一、TCP 协议概述](#一、TCP 协议概述)
- [二、TCP 连接建立:三次握手(Three-Way Handshake)](#二、TCP 连接建立:三次握手(Three-Way Handshake))
-
- [1. 过程解析](#1. 过程解析)
- [2. 为什么是三次?](#2. 为什么是三次?)
- [三、TCP 连接释放:四次挥手(Four-Way Handshake)](#三、TCP 连接释放:四次挥手(Four-Way Handshake))
-
- [1. 过程解析](#1. 过程解析)
- [2. TIME_WAIT 状态的作用](#2. TIME_WAIT 状态的作用)
- [四、TCP 可靠传输机制(扩展版)](#四、TCP 可靠传输机制(扩展版))
-
- [1. 滑动窗口(Sliding Window)](#1. 滑动窗口(Sliding Window))
- [2. 拥塞控制(代码实现)](#2. 拥塞控制(代码实现))
- [五、TCP 状态机与关键状态(扩展版)](#五、TCP 状态机与关键状态(扩展版))
-
- [1. 状态机详解](#1. 状态机详解)
- 六、半连接与全连接队列(扩展版)
-
- [1. Linux 系统调优](#1. Linux 系统调优)
- [2. SYN Flood 攻击防御代码](#2. SYN Flood 攻击防御代码)
- 七、字节流与序列号(扩展版)
-
- [1. 粘包问题解决方案](#1. 粘包问题解决方案)
- [八、TCP 优化与常见问题(扩展版)](#八、TCP 优化与常见问题(扩展版))
-
- [1. Nagle 算法与延迟确认](#1. Nagle 算法与延迟确认)
- [2. TIME_WAIT 优化](#2. TIME_WAIT 优化)
- [九、TCP 编程实战(新增章节)](#九、TCP 编程实战(新增章节))
-
- [1. Python 实现 TCP 服务器与客户端](#1. Python 实现 TCP 服务器与客户端)
- [2. 多线程与异步 TCP 服务器对比](#2. 多线程与异步 TCP 服务器对比)
- [十、TCP 性能调优实战(新增章节)](#十、TCP 性能调优实战(新增章节))
-
- [1. Linux 系统参数优化清单](#1. Linux 系统参数优化清单)
- [2. 网络抓包分析(Wireshark)](#2. 网络抓包分析(Wireshark))
- [十一、TCP 面试高频问题](#十一、TCP 面试高频问题)
-
- [为什么 TCP 连接是三次握手,而关闭是四次挥手?](#为什么 TCP 连接是三次握手,而关闭是四次挥手?)
- [TIME_WAIT 状态存在的意义?](#TIME_WAIT 状态存在的意义?)
- [如何排查 TCP 连接超时问题?](#如何排查 TCP 连接超时问题?)
- [TCP 和 UDP 的本质区别?](#TCP 和 UDP 的本质区别?)
- 十二、总结
一、TCP 协议概述
TCP(Transmission ControlProtocol)是传输层的面向连接、可靠的字节流协议,通过三次握手建立连接,四次挥手释放连接,利用滑动窗口、拥塞控制等机制保证数据可靠传输,适用于对可靠性要求高的场景(如HTTP、FTP)。
二、TCP 连接建立:三次握手(Three-Way Handshake)
1. 过程解析
第一次握手(SYN):客户端发送 SYN 包(SEQ=x),进入 SYN_SENT 状态。
第二次握手(SYN+ACK):服务端回复 SYN(SEQ=y)和 ACK(ACK=x+1),进入 SYN_RCVD 状态。
第三次握手(ACK):客户端回复 ACK(ACK=y+1),双方进入 ESTABLISHED 状态。
2. 为什么是三次?
双向确认:确保双方收发能力正常。若仅两次握手,服务端无法验证客户端是否收到 ACK(旧连接残留的 SYN 包可能导致误连)。三次握手通过双向确认,避免无效连接。
三、TCP 连接释放:四次挥手(Four-Way Handshake)
1. 过程解析
第一次挥手(FIN):主动关闭方发送 FIN(SEQ=u),进入 FIN_WAIT_1。
第二次挥手(ACK):被动关闭方回复 ACK(ACK=u+1),进入 CLOSE_WAIT;主动关闭方进入 FIN_WAIT_2。
第三次挥手(FIN):被动关闭方发送 FIN(SEQ=v),进入 LAST_ACK。
第四次挥手(ACK):主动关闭方回复 ACK(ACK=v+1),进入 TIME_WAIT(等待 2MSL 后关闭);被动关闭方收到 ACK 后关闭。
2. TIME_WAIT 状态的作用
确保 ACK 可靠:若最后一个 ACK 丢失,被动关闭方会重发 FIN,TIME_WAIT 期间可重传 ACK。
避免旧连接干扰:2MSL(报文最大生存时间)确保网络中残留的旧报文过期,不影响新连接。
四、TCP 可靠传输机制(扩展版)
1. 滑动窗口(Sliding Window)
窗口动态调整示例:
发送方窗口大小 = min (拥塞窗口,接收窗口)
// 发送方视角:窗口随ACK动态变化
已发送未确认:[1000-1999] // 已发送SEQ=1000-1999的数据包
可发送未发送:[2000-2999] // 窗口大小=2000字节,接收方允许发送 未发送:[3000-∞] // 超出当前窗口范围
代码模拟滑动窗口:
python
class SlidingWindow:
def __init__(self, window_size):
self.window_size = window_size # 窗口大小
self.base = 0 # 已发送未确认的第一个字节
self.next_seq = 0 # 下一个待发送的字节
def can_send(self, seq):
return seq < self.base + self.window_size
def receive_ack(self, ack):
if ack > self.base:
self.base = ack # 窗口滑动
# 触发窗口内未发送数据的发送
2. 拥塞控制(代码实现)
Python 模拟拥塞控制算法:
python
class CongestionControl:
def __init__(self):
self.cwnd = 1 # 初始拥塞窗口
self.ssthresh = 65535 # 慢启动阈值
def slow_start(self):
if self.cwnd < self.ssthresh:
self.cwnd += 1 # 指数增长
else:
self.congestion_avoidance()
def congestion_avoidance(self):
self.cwnd += 1/self.cwnd # 线性增长
def handle_loss(self):
self.ssthresh = max(2, self.cwnd/2)
self.cwnd = 1 # 发生丢包,重置窗口
五、TCP 状态机与关键状态(扩展版)
1. 状态机详解
TCP 状态转移图: 客户端: CLOSED → SYN_SENT → ESTABLISHED → FIN_WAIT_1 →
FIN_WAIT_2 → TIME_WAIT → CLOSED 服务端: CLOSED → LISTEN → SYN_RCVD →
ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
关键状态代码示例:
python
# 模拟TCP状态机(简化版)
class TCPStateMachine:
def __init__(self):
self.state = "CLOSED"
def process_event(self, event):
if self.state == "CLOSED":
if event == "主动打开":
self.state = "SYN_SENT"
# 发送SYN包
elif self.state == "SYN_SENT":
if event == "收到SYN+ACK":
self.state = "ESTABLISHED"
# 发送ACK包
# 其他状态转移...
六、半连接与全连接队列(扩展版)
1. Linux 系统调优
查看和调整队列大小:
bash
# 查看半连接队列大小
sysctl net.ipv4.tcp_max_syn_backlog # 默认值通常为128
# 查看全连接队列大小上限
sysctl net.core.somaxconn # 默认值通常为128
# 临时调整(重启失效)
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
sysctl -w net.core.somaxconn=4096
# 永久调整:修改/etc/sysctl.conf后执行sysctl -p
net.ipv4.tcp_max_syn_backlog = 4096
net.core.somaxconn = 4096
2. SYN Flood 攻击防御代码
python
Python 模拟 SYN Cookies 生成
import time
import hashlib
def generate_syn_cookie(ip, port, timestamp):
# 实际实现更复杂,包含秘密值和时间戳
secret = "server_secret_key"
data = f"{ip}{port}{timestamp}{secret}"
return hashlib.md5(data.encode()).hexdigest()[:8]
七、字节流与序列号(扩展版)
1. 粘包问题解决方案
python
定长协议实现:
import struct
def send_fixed_length(sock, data, length=1024):
# 填充或截断数据到固定长度
data = data.ljust(length, b'\x00')
sock.sendall(data)
def recv_fixed_length(sock, length=1024):
data = b''
while len(data) < length:
chunk = sock.recv(length - len(data))
if not chunk:
break
data += chunk
return data
长度前缀协议实现:
python
def send_with_length(sock, data):
# 先发送4字节的长度信息
length = len(data)
sock.sendall(struct.pack('!I', length))
sock.sendall(data)
def recv_with_length(sock):
# 先接收4字节的长度信息
length_data = sock.recv(4)
if len(length_data) < 4:
return None
length = struct.unpack('!I', length_data)[0]
# 再接收指定长度的数据
data = b''
while len(data) < length:
chunk = sock.recv(length - len(data))
if not chunk:
break
data += chunk
return data
八、TCP 优化与常见问题(扩展版)
1. Nagle 算法与延迟确认
Python socket 禁用 Nagle 算法:
python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 禁用Nagl
延迟确认示例:
2. TIME_WAIT 优化
调整 Linux 系统参数:
bash
# 启用TIME_WAIT状态重用
sysctl -w net.ipv4.tcp_tw_reuse=1
# 缩短TIME_WAIT超时时间(默认2MSL=120秒)
sysctl -w net.ipv4.tcp_fin_timeout=30
九、TCP 编程实战(新增章节)
1. Python 实现 TCP 服务器与客户端
python
# TCP服务器示例
import socket
import threading
def handle_client(client_socket):
try:
while True:
data = client_socket.recv(1024)
if not data:
break
client_socket.sendall(b"Server received: " + data)
finally:
client_socket.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8888))
server.listen(5) # 全连接队列大小
print("Server listening on port 8888...")
while True:
client, addr = server.accept()
print(f"Accepted connection from {addr}")
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start()
python
# TCP客户端示例
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8888))
try:
client.sendall(b"Hello, server!")
response = client.recv(1024)
print(f"Received from server: {response.decode()}")
finally:
client.close()
2. 多线程与异步 TCP 服务器对比
多线程服务器(阻塞 IO):
优点:实现简单;缺点:线程开销大,连接数受限于线程数量。
异步服务器(非阻塞 IO):
python
# 使用asyncio实现异步TCP服务器
import asyncio
async def handle_echo(reader, writer):
data = await reader.read(1024)
writer.write(b"Async server received: " + data)
await writer.drain()
writer.close()
async def main():
server = await asyncio.start_server(handle_echo, 'localhost', 8888)
async with server:
await server.serve_forever()
asyncio.run(main())
十、TCP 性能调优实战(新增章节)
1. Linux 系统参数优化清单
python
# 增大TCP接收/发送缓冲区
sysctl -w net.ipv4.tcp_rmem="4096 87380 67108864" # 最小、默认、最大接收窗口
sysctl -w net.ipv4.tcp_wmem="4096 65536 67108864" # 最小、默认、最大发送窗口
# 启用TCP窗口缩放(RFC 1323)
sysctl -w net.ipv4.tcp_window_scaling=1
# 启用TCP时间戳(用于PAWS和RTT计算)
sysctl -w net.ipv4.tcp_timestamps=1
# 优化TCP重传策略
sysctl -w net.ipv4.tcp_syn_retries=3 # SYN发送重试次数
sysctl -w net.ipv4.tcp_retries2=8 # 数据段重传次数
2. 网络抓包分析(Wireshark)
过滤表达式示例: tcp.port == 80 # 过滤TCP 80端口 tcp.flags.syn
== 1 and tcp.flags.ack == 0 # 过滤SYN包 tcp.analysis.retransmission # 过滤重传包
十一、TCP 面试高频问题
为什么 TCP 连接是三次握手,而关闭是四次挥手?
答:建立连接时,服务端的 SYN 和 ACK 可合并发送;关闭时,被动方可能需延迟发送 FIN(如等待数据处理完毕),因此需四次。
TIME_WAIT 状态存在的意义?
答:
确保最后一个 ACK 可靠到达(若丢失,被动方会重发 FIN)。
避免旧连接的延迟报文影响新连接(等待 2MSL 确保所有旧报文过期)。
如何排查 TCP 连接超时问题?
答:
检查防火墙规则(是否拦截 SYN 包)。
使用 netstat -s 查看 TCP 统计信息(如 SYN_RECV 队列溢出)。
Wireshark 抓包分析(是否存在 SYN 包无响应)。
TCP 和 UDP 的本质区别?
答:TCP 提供可靠、面向连接、有序的字节流服务;UDP 提供无连接、不可靠、快速的数据报服务。选择取决于应用场景(如 HTTP 需 TCP,实时游戏可用 UDP)。
十二、总结
TCP 协议通过精巧的设计(三次握手、滑动窗口、拥塞控制等),在不可靠的网络层上构建了可靠的传输服务。理解其原理不仅能解决网络故障(如连接超时、丢包),还能指导高性能网络应用开发(如优化服务器参数、设计高效协议)。
在实际工作中,需根据业务场景权衡 TCP 与 UDP 的选择,并针对具体问题(如粘包、TIME_WAIT 堆积)设计解决方案。掌握 TCP 协议,是成为优秀后端工程师、网络工程师的必备技能。