【网络】TCP/IP协议深度解析从连接建立到数据传输前言TCP/IP协议栈是互联网的基础几乎所有的网络应用都建立在TCP/IP之上。深入理解TCP/IP协议的工作原理对于网络编程、网络故障排查、系统性能优化都是必不可少的知识。作为AI程序员我们经常需要处理分布式计算、API调用、模型部署等网络相关任务对TCP/IP协议的深入理解能帮助我们更好地设计和优化这些系统。本文将从TCP/IP协议栈的整体架构出发深入讲解TCP协议的三次握手、四次挥手、可靠传输机制、拥塞控制算法等核心内容并通过大量的图示和实战代码帮助读者建立对TCP/IP协议的深刻理解。一、TCP/IP协议栈概述1.1 协议栈的分层结构TCP/IP协议栈采用分层设计主要分为四层链路层、网络层、传输层和应用层。┌────────────────────────────────────────────┐ │ 应用层 (Application) │ │ HTTP, FTP, SMTP, DNS, SSH, WebSocket │ ├────────────────────────────────────────────┤ │ 传输层 (Transport) │ │ TCP, UDP, QUIC │ ├────────────────────────────────────────────┤ │ 网络层 (Internet) │ │ IP, ICMP, ARP, BGP │ ├────────────────────────────────────────────┤ │ 链路层 (Link) │ │ Ethernet, WiFi, PPP, MPLS │ └────────────────────────────────────────────┘各层的职责应用层负责应用程序间的通信处理高层协议、数据格式等传输层提供端到端的通信服务负责进程间的数据传输网络层负责主机间的路由和转发实现不同网络间的互联链路层负责相邻节点间的数据传输处理物理地址寻址1.2 数据封装过程数据在网络中传输时每一层都会添加自己的头部信息这个过程称为封装。# 数据封装示意 # 应用层数据 - TCP头 - IP头 - Ethernet头 - 物理传输 - Ethernet尾 - IP头 - TCP头 - 应用层数据 # 实际抓包观察 # 使用Wireshark或tcpdump抓包可以看到完整的分层结构 # tcpdump示例 # sudo tcpdump -i eth0 -nn port 80 -X # -i: 指定网卡 # -nn: 不解析域名和端口名 # -X: 以十六进制和ASCII显示数据包内容1.3 socket编程基础socket是应用层与传输层之间的编程接口import socket import threading # TCP服务器 def tcp_server(): TCP服务器示例 server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) # socket.AF_INET: IPv4 # socket.SOCK_STREAM: TCP # 设置地址复用选项 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((0.0.0.0, 8080)) # 绑定地址和端口 server_socket.listen(128) # 监听连接 print(Server listening on port 8080...) while True: client_socket, client_addr server_socket.accept() print(fConnection from {client_addr}) # 为每个客户端创建线程处理 thread threading.Thread(targethandle_client, args(client_socket, client_addr)) thread.daemon True thread.start() def handle_client(client_socket, client_addr): 处理客户端请求 try: while True: data client_socket.recv(1024) # 接收数据 if not data: break print(fReceived from {client_addr}: {data.decode()}) # 原样返回 client_socket.sendall(data) except Exception as e: print(fError handling client {client_addr}: {e}) finally: client_socket.close() print(fConnection closed: {client_addr}) # TCP客户端 def tcp_client(): TCP客户端示例 client_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((127.0.0.1, 8080)) # 连接服务器 print(Connected to server) # 发送数据 client_socket.sendall(bHello, TCP Server!) # 接收响应 response client_socket.recv(1024) print(fReceived from server: {response.decode()}) client_socket.close() # UDP服务器 def udp_server(): UDP服务器示例 server_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket.SOCK_DGRAM: UDP server_socket.bind((0.0.0.0, 8080)) print(UDP Server listening on port 8080...) while True: data, client_addr server_socket.recvfrom(1024) # 接收数据和地址 print(fReceived from {client_addr}: {data.decode()}) # 发送响应 server_socket.sendto(bHello, UDP Client!, client_addr) # UDP客户端 def udp_client(): UDP客户端示例 client_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.sendto(bHello, UDP Server!, (127.0.0.1, 8080)) data, server_addr client_socket.recvfrom(1024) print(fReceived from server: {data.decode()}) client_socket.close()二、TCP三次握手详解2.1 握手过程分析TCP是一种面向连接的协议在传输数据之前必须先建立连接。三次握手是TCP建立连接的过程客户端 服务器 │ │ │ ──────────── SYN, seqx ────────────────────────│ 第一次握手 │ │ (客户端发送SYN) │ │ │ ────────── SYNACK, seqy, ackx1 ─────────────│ 第二次握手 │ │ (服务器发送SYNACK) │ │ │ ──────────── ACK, seqx1, acky1 ─────────────│ 第三次握手 │ │ (客户端发送ACK) │ │ │ 建立连接完成 │ │ │第一次握手客户端发送SYN包SYN1, seqx进入SYN_SENT状态第二次握手服务器收到SYN后发送SYNACK包SYN1, ACK1, seqy, ackx1进入SYN_RCVD状态第三次握手客户端收到SYNACK后发送ACK包ACK1, seqx1, acky1进入ESTABLISHED状态# 使用socket理解三次握手 import socket # 当调用listen时服务器进入LISTEN状态 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((0.0.0.0, 8080)) server.listen(128) # 此时内核会创建两个队列 # 1. 半连接队列SYN队列存放SYN_RECV状态的连接 # 2. 全连接队列accept队列已完成三次握手的连接 # accept从全连接队列取出连接 client, addr server.accept()2.2 半连接队列与全连接队列# 查看服务器状态的示例 import socket import time def tcp_server_status(): 演示队列状态 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 9999)) server.listen(5) # listen的backlog参数影响accept队列大小 print(Server listening...) # 如果不accept客户端connect会怎样 # 客户端connect会成功因为内核完成了三次握手 # 但服务端进程还没有处理这个连接 # 模拟客户端快速发送大量连接 # 服务端accept不及时导致队列溢出2.3 ISN与序列号ISNInitial Sequence Number是TCP连接的初始序列号是一个随机生成的值。# 序列号的作用 # 1. 保证数据传输的可靠性 # 2. 保证数据按序接收 # 3. 防止历史报文被重复接收 # 序列号计算 # ISN是随机生成的现代系统使用半随机算法 # 后续序列号 ISN 已传输的字节数 # 示例序列号变化 # 假设ISN1000发送100字节数据 # 发送100字节后下一个序列号是1100 # 如果需要重传序列号仍然从1000开始 # Wireshark中查看序列号 # Relative Sequence Number: 相对序列号从0开始方便阅读 # Absolute Sequence Number: 绝对序列号真实的ISN2.4 三次握手的意义为什么是三次不是两次或四次# 如果是两次握手 # 客户端发送SYN服务器返回ACK # 问题如果ACK丢失客户端不知道服务器是否收到了SYN # 客户端认为连接建立成功但服务器不知道 # 客户端开始发送数据但服务器没有建立连接会导致错误 # 如果是四次握手 # 技术上可以但没必要 # 第二次握手可以拆分成SYNACK分开发送 # 但这样会降低效率而三次已经足够可靠 # 三次握手解决的问题 # 1. 双方都能确认对方的发送和接收能力正常 # 2. 双方都能协商确定初始序列号 # 3. 避免历史连接初始化混乱三、TCP四次挥手详解3.1 挥手过程分析TCP关闭连接需要四次挥手客户端 服务器 │ │ │ ──────────── FIN, sequ ────────────────────────│ 第一次挥手 │ │ (客户端发送FIN) │ ─────────── ACK, acku1 ───────────────────────│ 第二次挥手 │ │ (服务器发送ACK) │ │ (此时客户端-服务器方向关闭) │ │ │ 关闭半连接 │ │ │ │ ─────────── FIN, seqw ────────────────────────│ 第三次挥手 │ │ (服务器发送FIN) │ ──────────── ACK, ackw1 ───────────────────────│ 第四次挥手 │ │ (客户端发送ACK) │ │ │ 关闭完成 │ │ │ │ 客户端等待2MSL后关闭 │ │ │# socket关闭连接 # 方式1close() # 立即关闭连接发送FIN进入TIME_WAIT状态 # 方式2shutdown() # 优雅关闭可以只关闭一个方向 # shutdown(SHUT_RD): 关闭读 # shutdown(SHUT_WR): 关闭写 # shutdown(SHUT_RDWR): 关闭读写 def graceful_close(): 优雅关闭连接 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((0.0.0.0, 8080)) server.listen(128) client, addr server.accept() # 接收完数据后优雅关闭写端 data client.recv(1024) print(fReceived: {data}) # 发送响应 client.sendall(bOK) # 关闭写端告诉对方数据已发送完毕 client.shutdown(socket.SHUT_WR) # 可以继续接收剩余数据 try: remaining client.recv(1024) print(fRemaining: {remaining}) except: pass client.close()3.2 TIME_WAIT状态TIME_WAIT状态是TCP连接关闭后本地端等待的状态。# TIME_WAIT存在的原因 # 1. 保证最后的ACK能到达对方如果ACK丢失对端会重传FIN # 2. 让旧连接的报文在网络中完全消失避免影响新连接 # TIME_WAIT持续时间2MSLMaximum Segment Lifetime # MSL是报文在网络中的最大生存时间通常为30秒或60秒 # 2MSL 60-120秒 # 问题服务器在高并发下可能产生大量TIME_WAIT状态的连接 # 解决方案 # 1. 客户端主动关闭连接让客户端进入TIME_WAIT # 2. 设置socket选项SO_LINGER # 3. 开启TCP_TIMEWAIT_LENLinux内核参数 # 4. 使用连接池复用连接 # 优化TIME_WAIT import socket server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # SO_REUSEADDR允许绑定到TIME_WAIT状态的端口3.3 连接状态转换# TCP连接状态 # LISTEN: 服务器等待连接 # SYN_SENT: 客户端已发送SYN # SYN_RECV: 收到SYN并发送SYNACK # ESTABLISHED: 连接建立成功 # FIN_WAIT_1: 已发送FIN # FIN_WAIT_2: 收到ACK等待对方FIN # CLOSING: 双方同时关闭 # TIME_WAIT: 等待2MSL # CLOSE: 无连接 # 查看连接状态 # Linux: netstat -an | grep ESTABLISHED # 或: ss -tan state ESTABLISHED # Python获取连接状态 import socket def get_socket_state(sock): 获取socket状态 # 可以通过/proc/net/tcp查看 pass四、TCP可靠传输机制4.1 确认与重传TCP通过确认ACK和重传机制保证可靠传输# TCP可靠传输原理 # 1. 发送方发送数据等待ACK # 2. 接收方收到数据发送ACK确认 # 3. 如果发送方在超时时间内没收到ACK重传数据 # 超时时间计算 # TCP使用自适应算法计算RTTRound Trip Time # RTO (Retransmission Timeout) RTT 4 * RTTVAR # RTTVAR是RTT的偏差加权平均值 # 重传机制 # 1. 超时重传超过RTO没收到ACK就重传 # 2. 快速重传当收到3个重复ACK时说明报文丢失触发快速重传 # 示例快速重传 # 发送方发送 seq1000, 2000, 3000, 4000 # 接收方收到 seq1000, 2000, 3000seq4000丢失 # 接收方发送 ACK3000重复ACK # 发送方收到3个重复ACK立即重传 seq40004.2 流量控制TCP使用滑动窗口实现流量控制# 滑动窗口原理 # 发送方维护一个发送窗口 # 窗口内的数据可以连续发送无需等待ACK # 窗口大小由接收方的接收能力决定 # 窗口大小字段 # TCP头部有16位的Window Size字段 # 最大窗口大小 65535字节 # 使用Window Scaling选项可扩展到1GB # 零窗口 # 当接收方缓冲区满时Window Size0 # 发送方停止发送数据 # 接收方处理完后发送Window Update # 探测零窗口 # 发送方定期发送零窗口探测1字节数据 # 接收方回应当前的窗口大小4.3 拥塞控制TCP拥塞控制是保证网络稳定运行的关键机制# TCP拥塞控制四个算法 # 1. 慢启动Slow Start # 2. 拥塞避免Congestion Avoidance # 3. 快速重传Fast Retransmit # 4. 快速恢复Fast Recovery # 慢启动 # cwnd拥塞窗口从1个MSS开始 # 每收到一个ACKcwnd增加1个MSS # 指数增长直到达到ssthresh # 拥塞避免 # cwnd达到ssthresh后 # 每收到一个ACKcwnd增加 (MSS * MSS) / cwnd # 线性增长 # 快速重传与快速恢复 # 收到3个重复ACK时触发快速重传 # ssthresh cwnd / 2 # cwnd ssthresh 3 * MSS # 快速重传丢失的包然后进入快速恢复 # CUBIC算法Linux默认 # 使用三次函数调整cwnd # 更适应高带宽高延迟网络 # 查看TCP拥塞控制算法 # cat /proc/sys/net/ipv4/tcp_congestion_control # 调整拥塞控制算法 # sysctl -w net.ipv4.tcp_congestion_controlbic # sysctl -w net.ipv4.tcp_congestion_controlcubic4.4 Nagle算法与延迟确认# Nagle算法 # 目的减少小包发送提高网络效率 # 规则只有收到前一个数据的ACK才发送新数据 # 适用于Telnet等交互式应用 # 问题可能导致延迟 # 禁用Nagle算法 import socket tcp_client socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # TCP_NODELAY1: 禁用Nagle算法 # 延迟确认Delayed ACK # 收到数据后不立即发送ACK # 等待一段时间或等到有数据发送时一起发 # 减少ACK数量 # 相关参数 # /proc/sys/net/ipv4/tcp_delack_min # /proc/sys/net/ipv4/tcp_delayed_ack五、TCP高级特性5.1 TCP选项# TCP头部可选字段 # MSS (Maximum Segment Size): 最大报文段大小 # Window Scaling: 窗口扩大因子 # SACK Permitted: 选择性确认 # Timestamps: 时间戳 # PCP (Partial Order Connection Profile): 部分有序连接 # UTO (User Timeout): 用户超时 # 设置MSS # 在建立连接时协商 # 通常MTU - 40 MSS # 以太网MTU1500, MSS1460 # 启用SACK # 允许接收方只确认丢失的数据段 # 提高重传效率 import socket sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Linux默认启用SACK5.2 keepalive# TCP keepalive # 目的检测连接是否存活 # 机制长时间没有数据传输时发送探测包 # 三个参数Linux # tcp_keepalive_time: 多久没数据传输开始探测默认7200秒 # tcp_keepalive_probes: 探测次数默认9次 # tcp_keepalive_intvl: 探测间隔默认75秒 # 设置keepalive import socket sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 设置探测参数 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) # 应用层心跳 # 相比keepalive应用层心跳更可控 def heartbeat_check(sock, interval30): 应用层心跳检测 import time while True: try: sock.send(bPING) # 发送心跳 data sock.recv(1024) if data ! bPONG: # 处理异常 pass except: # 连接断开 break time.sleep(interval)5.3 多路复用# HTTP/1.1的keepalive # 一个TCP连接可以发送多个HTTP请求 # 但需要串行等待响应队头阻塞 # HTTP/2的多路复用 # 在一个TCP连接上并行发送多个请求/响应 # 解决队头阻塞问题 # WebSocket # 建立TCP连接后升级到WebSocket协议 # 全双工通信可随时发送数据 # gRPC基于HTTP/2 # 多路复用 二进制格式 流控 # Python异步IO import asyncio import socket async def async_tcp_client(): 异步TCP客户端 reader, writer await asyncio.open_connection(127.0.0.1, 8080) writer.write(bHello) await writer.drain() # 等待数据发送完成 data await reader.read(1024) print(fReceived: {data}) writer.close() await writer.wait_closed()六、网络抓包与分析6.1 tcpdump使用# 基本用法 sudo tcpdump -i eth0 port 80 # 抓取80端口的包 sudo tcpdump -i eth0 host 192.168.1.1 # 抓取与特定IP相关的包 sudo tcpdump -i eth0 -n port 8080 # 不解析域名和端口名 # 保存到文件 sudo tcpdump -i eth0 -w capture.pcap port 80 # -w: 保存到文件 # .pcap文件可以用Wireshark分析 # 读取文件 tcpdump -r capture.pcap # 常用选项 # -i: 指定网卡 # -n: 不解析域名 # -nn: 不解析域名和端口 # -v: 详细输出-vv更详细 # -c: 捕获指定数量的包 # -X: 以十六进制和ASCII显示 # -A: 只显示ASCII # -e: 显示以太网头部 # -t: 不显示时间戳 # 表达式 # port 80: 端口80 # host 192.168.1.1: 特定IP # tcp: 只TCP # udp: 只UDP # icmp: 只ICMP # and/or/not: 逻辑组合 # 示例 sudo tcpdump -i eth0 -nn -vv tcp port 80 and (host 192.168.1.1 or host 192.168.1.2)6.2 Wireshark使用# Wireshark是图形化的网络协议分析器 # 安装sudo apt install wireshark # 常用过滤器 # 协议过滤器: tcp, udp, http, dns, icmp # 端口过滤器: tcp.port 80, udp.port 53 # IP过滤器: ip.addr 192.168.1.1, ip.src x.x.x.x # 逻辑组合: and, or, not # TCP追踪 # 右键 - Follow - TCP Stream # 可以看到完整的TCP会话 # 过滤器示例 # http.request.method GET # GET请求 # http.response.code 200 # 200响应 # tcp.analysis.retransmission # 重传包 # tcp.analysis.lost_segment # 丢失的段6.3 Python网络编程实战import socket import select import struct def create_tcp_packet(src_port, dst_port, seq, ack, flags, datab): 创建TCP数据包 # TCP头部格式 # src_port(2) dst_port(2) seq(4) ack(4) offset_flags(2) window(2) checksum(2) urgent(2) src_port struct.pack(!H, src_port) dst_port struct.pack(!H, dst_port) seq_num struct.pack(!I, seq) ack_num struct.pack(!I, ack) # 偏移量5 * 4 20字节 5个32位字和标志 offset (5 4) | 0 flags_byte flags offset_flags struct.pack(!H, (offset 8) | flags_byte) window struct.pack(!H, 65535) # 窗口大小 checksum struct.pack(!H, 0) # 校验和稍后计算 urgent struct.pack(!H, 0) header src_port dst_port seq_num ack_num offset_flags window checksum urgent return header data def parse_tcp_packet(packet): 解析TCP数据包 if len(packet) 20: return None src_port, dst_port struct.unpack(!HH, packet[0:4]) seq, ack struct.unpack(!II, packet[4:12]) offset_flags struct.unpack(!H, packet[12:14])[0] offset (offset_flags 8) 4 flags offset_flags 0xFF # 提取标志位 fin flags 0x01 syn (flags 1) 0x01 rst (flags 2) 0x01 psh (flags 3) 0x01 ack (flags 4) 0x01 ece (flags 5) 0x01 urg (flags 6) 0x01 return { src_port: src_port, dst_port: dst_port, seq: seq, ack: ack, flags: { FIN: fin, SYN: syn, RST: rst, PSH: psh, ACK: ack, ECE: ece, URG: urg }, data: packet[offset * 4:] } def raw_tcp_client(): 原始TCP客户端需要root权限 # 创建原始套接字 sock socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP) # socket.IPPROTO_RAW: 包含IP头部 # socket.IPPROTO_TCP: 包含TCP头部 # 设置IP选项 sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # 目标地址 dst_ip 93.184.216.34 # example.com dst_port 80 # 构造数据包 src_port 12345 seq 0 ack 0 flags 0x02 # SYN packet create_tcp_packet(src_port, dst_port, seq, ack, flags) # 发送 sock.sendto(packet, (dst_ip, 0)) # 接收响应 sock.settimeout(5) try: response, addr sock.recvfrom(65535) print(fReceived from {addr}) tcp_info parse_tcp_packet(response[20:]) # 跳过IP头部 print(tcp_info) except socket.timeout: print(Timeout) sock.close()七、实战案例构建高性能TCP服务7.1 多进程TCP服务器import socket import multiprocessing import signal import time def handle_client(conn, addr): 处理客户端连接 print(fProcess {multiprocessing.current_process().pid} handling {addr}) try: while True: data conn.recv(1024) if not data: break conn.sendall(data) except Exception as e: print(fError: {e}) finally: conn.close() def multi_process_server(): 多进程TCP服务器 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 8000)) server.listen(128) print(fServer listening on port 8000 (PID: {multiprocessing.current_process().pid})) # 忽略子进程终止信号 signal.signal(signal.SIGCHLD, signal.SIG_IGN) while True: conn, addr server.accept() print(fNew connection from {addr}) # 创建子进程处理连接 process multiprocessing.Process(targethandle_client, args(conn, addr)) process.daemon True process.start() # 父进程关闭conn子进程已经有副本 conn.close() # 测试 if __name__ __main__: multi_process_server()7.2 单进程并发服务器IO多路复用import socket import selectors # 使用select实现IO多路复用 def select_server(): 使用select的IO多路复用服务器 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 8000)) server.listen(128) # 注册server socket到select inputs [server] outputs [] message_queues {} print(Server listening on port 8000...) while inputs: # 等待可读事件 readable, writable, exceptional select.select(inputs, outputs, inputs) for sock in readable: if sock is server: # 新连接 conn, addr sock.accept() print(fNew connection: {addr}) conn.setblocking(False) # 非阻塞 inputs.append(conn) message_queues[conn] [] else: # 数据可读 try: data sock.recv(1024) if data: print(fReceived from {sock.getpeername()}: {data}) # 加入写队列 message_queues[sock].append(data) if sock not in outputs: outputs.append(sock) else: # 连接关闭 print(fConnection closed: {sock.getpeername()}) inputs.remove(sock) if sock in outputs: outputs.remove(sock) del message_queues[sock] sock.close() except Exception as e: print(fError: {e}) inputs.remove(sock) sock.close() for sock in writable: # 发送数据 if sock in message_queues and message_queues[sock]: data message_queues[sock].pop(0) try: sock.sendall(data) if not message_queues[sock]: outputs.remove(sock) except Exception as e: print(fSend error: {e}) for sock in exceptional: # 异常处理 print(fException on {sock.getpeername()}) inputs.remove(sock) if sock in outputs: outputs.remove(sock) sock.close() del message_queues[sock]7.3 使用epoll的服务器import socket import selectors def epoll_server(): 使用epoll的高性能服务器Linux特有 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 8000)) server.listen(128) # 创建epoll对象 epoll selectors.EpollSelector() # 注册server socket server.setblocking(False) epoll.register(server, selectors.EVENT_READ, None) print(Server listening on port 8000...) connections {} while True: # 等待事件 events epoll.select(timeout1) for fd, event in events: sock connections.get(fd) if fd in connections else None if sock is None and event selectors.EVENT_READ: # server socket可读表示有新连接 if fd server.fileno(): conn, addr server.accept() print(fNew connection: {addr}) conn.setblocking(False) # 注册新连接 connections[conn.fileno()] conn epoll.register(conn, selectors.EVENT_READ, None) continue if event selectors.EVENT_READ: # 可读 try: data sock.recv(1024) if data: print(fReceived: {data}) sock.sendall(data) else: # 关闭连接 print(fConnection closed) epoll.unregister(sock) sock.close() del connections[sock.fileno()] except Exception as e: print(fError: {e}) epoll.unregister(sock) sock.close() del connections[sock.fileno()] if event selectors.EVENT_WRITE: # 可写 pass # 使用selectors模块跨平台自动选择最佳实现 def modern_select_server(): 使用selectors模块的服务器 server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 8000)) server.listen(128) selector selectors.DefaultSelector() server.setblocking(False) def accept_handler(sock): conn, addr sock.accept() print(fNew connection: {addr}) conn.setblocking(False) selector.register(conn, selectors.EVENT_READ, read_handler) def read_handler(conn): data conn.recv(1024) if data: print(fReceived: {data}) conn.sendall(data) else: print(Connection closed) selector.unregister(conn) conn.close() selector.register(server, selectors.EVENT_READ, accept_handler) print(Server listening on port 8000...) while True: events selector.select(timeout1) for key, event in events: callback key.data callback(key.fileobj)八、总结TCP/IP协议是网络通信的基础深入理解其工作原理对于网络编程和系统优化至关重要。本文深入讲解了协议栈结构理解四层模型和数据封装过程三次握手连接的建立过程和状态转换四次挥手连接关闭过程和TIME_WAIT状态可靠传输确认重传、流量控制、拥塞控制机制高级特性TCP选项、keepalive、多路复用抓包分析使用tcpdump和Wireshark分析网络问题实战应用构建高性能TCP服务器的多种模式作为AI程序员理解TCP/IP协议能帮助我们更好地设计和优化分布式系统排查网络相关的问题选择合适的通信协议和参数配置理解gRPC、WebSocket等高层协议的底层原理希望本文能帮助读者建立起对TCP/IP协议的深入理解为今后的网络编程工作打下坚实基础。