一次TCP通信的函数链路是socket()→bind()/connect()→listen()+accept()→send()/recv(),每个调用对应内核状态操作,如socket()初始化TCB、connect()触发三次握手、send()仅表示数据入内核缓冲区。

socket() 到 send()/recv():一次 TCP 通信的函数链路
Linux 网络通信不是“直接发数据”,而是由内核协议栈驱动的一系列状态流转。从应用层发起,关键路径是:socket() → bind()(服务端)或 connect()(客户端)→ listen() + accept()(服务端)→ send()/recv()。每个调用背后都对应内核中一个数据结构操作:比如 socket() 分配 fd 并初始化 TCB(传输控制块),connect() 实际触发三次握手,但函数阻塞直到第三次 ACK 收到才返回。
- 服务端必须显式调用
bind()绑定本地 IP 和端口,否则内核随机分配(通常不满足业务需求) -
listen()的backlog参数不是连接总数,而是「已完成三次握手、等待accept()取走」的连接队列长度;设太小会导致客户端connect()返回ECONNREFUSED -
send()成功只表示数据拷贝进内核写缓冲区,不等于对方已收到;recv()返回 0 表示对端调用了close()或shutdown(),这是正常断连信号
数据怎么从用户内存跑到网线?——封装与分段的真实过程
你调用 send() 写入 10KB 数据,它不会原样发出去。内核会按 MTU(通常 1500 字节)切片,每片加上 TCP 头(20+ 字节)、IP 头(20+ 字节)、以太网头(14 字节)和尾(4 字节 CRC)。最终发到网卡的是一个个 sk_buff 结构体组成的帧,不是你的原始 buf。
- 应用层无感知分片:TCP 自动处理分段和重组,但“黏包”问题正源于此——
recv()可能一次读到多个逻辑消息,或一个消息被拆成多次返回 - 校验和由网卡硬件计算(如启用 TSO/GSO),不是 CPU 算的;若禁用硬件卸载(
ethtool -K eth0 tx off),性能可能下降 10%~30% -
路由决策发生在 IP 层:内核查
route -n或ip route输出的路由表,匹配目标 IP 后决定走哪个接口、下一跳是谁
三次握手失败时,你会看到什么错误?
客户端 connect() 超时或失败,常见现象不是“连不上”,而是具体错误码暴露了哪一环断了:
华友协同办公管理系统(华友OA),基于微软最新的.net 2.0平台和SQL Server数据库,集成强大的Ajax技术,采用多层分布式架构,实现统一办公平台,功能强大、价格便宜,是适用于企事业单位的通用型网络协同办公系统。 系统秉承协同办公的思想,集成即时通讯、日记管理、通知管理、邮件管理、新闻、考勤管理、短信管理、个人文件柜、日程安排、工作计划、工作日清、通讯录、公文流转、论坛、在线调查、
-
Connection refused (ECONNREFUSED):服务器没在监听(端口未bind()+listen()),或防火墙 DROP 了 SYN 包 -
Operation timed out (ETIMEDOUT):SYN 发出去了,但没收到 SYN+ACK —— 可能服务器宕机、路由不通、中间设备丢包,或服务器 SYN 队列满(netstat -s | grep "SYNs to LISTEN sockets dropped") -
No route to host (EHOSTUNREACH):本地路由表找不到目标网络,比如目标 IP 是私有地址但本机不在同一子网,且没配置网关
/etc/network/interfaces 和协议栈的关系很浅
这个文件只管「网卡配什么 IP」,属于网络层初始化配置;它不参与 TCP 连接建立、不控制 socket 行为、也不影响三次握手流程。改完它需要 ifdown/ifup 或重启网络服务,但已建立的 TCP 连接不受影响——因为连接状态保存在内核的 socket 结构里,和接口配置是解耦的。
真正影响通信的是运行时内核参数,比如:net.ipv4.tcp_tw_reuse = 1 允许 TIME_WAIT 套接字重用,避免高并发短连接耗尽端口;net.core.somaxconn 控制 listen() 的最大全连接队列长度。这些比静态配置文件更直接决定通信能否撑住流量。









