笔记 / 返回列表

Linux 与工程工具

Linux TCP Socket 基础:从监听到收发

梳理 TCP 服务端和客户端的系统调用顺序,并说明字节流、错误处理和资源释放这些最容易被忽略的边界。

问题
梳理 TCP 服务端和客户端的系统调用顺序,并说明字节流、错误处理和资源释放这些最容易被忽略的边界。
环境
Linux · Socket · TCP · 网络编程 · C
状态
待发布
首次发布
2025年10月17日
最后更新
2026年8月10日
预计阅读
3 分钟

生命周期说明(2026-08-10):本文由 2025 年的 TCP 学习笔记重写。原笔记中的内网地址、一次性示例和不完整错误路径均未公开保留;本文只说明稳定的接口顺序与实现边界。具体 API 细节以当前系统手册页为准。

Socket 在程序中的位置

Socket 是操作系统提供的通信接口。TCP 程序通常以客户端—服务端的方式建立连接:服务端等待连接,客户端主动连接;连接建立后,双方都可以读取和写入字节流。

TCP 提供的是有序字节流,不是“按一次 send() 对应一次 recv()”的消息队列。因此,应用协议必须自行定义消息边界,例如固定长度头、长度字段或明确分隔符。

服务端的基本顺序

一个最小 TCP 服务端通常按下列顺序运行:

  1. socket() 创建套接字;
  2. bind() 绑定本地地址和端口;
  3. listen() 将套接字置为监听状态;
  4. accept() 取得一条已建立连接对应的新文件描述符;
  5. 对新描述符调用 recv()/read() 和 send()/write();
  6. 使用完毕后分别 close() 已连接描述符和监听描述符。

监听套接字只负责接受连接;accept() 返回的描述符才对应某个客户端。服务端要同时处理多个客户端时,可以选择线程、进程、事件循环或异步 I/O,但每种方案都需要明确并发与关闭策略。

客户端的基本顺序

客户端的路径更短:

  1. socket() 创建套接字;
  2. 准备服务端地址;
  3. connect() 发起连接;
  4. 读写数据;
  5. close() 释放描述符。

在可移植程序中,优先使用 getaddrinfo() 解析主机和端口,再根据返回结果建立连接;这样可避免把 IPv4 地址或局部网络参数写死在源码中。

收发与关闭的关键边界

  • recv() 返回正数表示读到的字节数;返回 0 表示对端已正常关闭写端;返回负数才需要根据 errno 判断失败原因。
  • send() 或 write() 可以只写入部分数据。需要完整发送一条消息时,应循环写入直到全部字节完成或发生不可恢复错误。
  • 网络数据不保证以字符串结尾。若要打印或解析,应以实际返回的长度为界,不要假设缓冲区自动带有 \0。
  • 端口号与地址应使用网络字节序转换:端口使用 htons()/ntohs(),IPv4 地址按所用接口转换或打印。
  • 任何提前返回路径都应关闭已取得的文件描述符;连接较多时还应限制缓冲区、超时和并发数。

本地验证建议

先只在回环地址上测试:服务端监听一个未被占用的高位端口,客户端连接 127.0.0.1 或 ::1。依次验证连接失败、正常收发、对端关闭、超长输入和服务端重启。确认消息边界与错误日志符合预期后,再考虑局域网监听或更复杂的协议。

这类程序会处理来自网络的输入。公开示例或生产代码应增加长度限制、超时、错误日志和协议校验,而不是直接把接收缓冲区交给字符串或业务逻辑使用。

相关内容

SEARCH / QUICK JUMP

搜索工程档案

↑↓ 选择 · Enter 打开 · Esc 关闭