一、项目简介
本项目是一个基于 Linux实现的 TCP 多客户端与服务器通信程序,主要包含:
tcp_server:TCP 服务端程序,监听客户端连接,接收客户端数据,维护在线客户端列表。
tcp_client:TCP 客户端程序,连接服务端,支持从标准输入发送消息。
自定义应用层协议:每个数据包由 2 字节 payload 长度 和 payload 内容 组成。
可靠性机制:发送超时、重试、心跳检测、客户端自动重连。
多客户端管理:服务端维护客户端链表,可统计并打印当前在线客户端数量。
服务端默认监听端口为 8080,客户端默认连接 服务器ip "127.0.0.1:8080"。
二、目录结构
Tcpserver_project00v1/ ├── Makefile ├── README.md ├── include/ │ ├── common.h # 公共配置、端口、缓冲区、超时和错误码 │ ├── tcp_client_list.h # 服务端客户端链表管理接口 │ ├── tcp_protocol.h # 协议打包和解析接口 │ ├── tcp_reliability.h # keep-alive、超时、重试接口 │ └── tcp_server.h # 服务端启动接口 ├── src/ │ ├── main.c # 服务端入口 │ ├── tcp_server.c # 服务端主循环、连接接入、select 事件处理 │ ├── tcp_client.c # 客户端入口和通信逻辑 │ ├── tcp_client_list.c # 客户端链表增删查和在线列表打印 │ ├── tcp_protocol.c # 协议解析、业务响应、ACK 回复 │ └── tcp_reliability.c # socket keep-alive、超时和重试发送 └── build/ # 编译生成的中间文件主要模块职责
tcp_server.c: 初始化监听 socket,使用 select 处理新连接和客户端数据,检查客户端空闲超时。
tcp_client.c: 连接服务端,读取用户输入,发送业务数据和心跳,处理 ACK 和自动重连。
tcp_protocol.c: 实现协议打包、拆包、粘包半包处理、业务回复、心跳 ACK 和数据 ACK。
tcp_client_list.c: 维护服务端在线客户端链表。
tcp_reliability.c: 封装 socket keep-alive、收发超时和发送重试。
common.h: 集中管理端口、缓冲区大小、超时、重试次数和错误码。
三、运行环境
项目代码使用 Linux socket API,适合在 Linux 或 ARM Linux 环境运行。
编译依赖:
make
ARM 交叉编译工具链二选一:
arm-linux-gnueabihf-gcc
arm-linux-gcc
Makefile 会自动检测可用的 ARM 交叉编译器。如果两个工具链都不存在,执行 make 会报错。
四、编译说明
在项目根目录执行:
make
编译成功后会生成两个arm32位的可执行文件:
tcp_server tcp_client
清理编译产物:
make clean
如果需要在普通 x86 Linux 主机上本地测试,可以临时修改 Makefile 中的 CC 和 CFLAGS,例如使用:
CC = gcc
CFLAGS = -Wall -O2 -I./include
LDFLAGS =
五、使用说明
1. 启动服务端
在一台终端中运行:
./tcp_server
正常启动后会输出类似信息: TCP服务器启动,监听中... 8080
2. 启动客户端
在另一台终端中运行:
./tcp_client
连接成功后会输出:
已连接到服务器 127.0.0.1:8080
之后可以在客户端终端直接输入文本并回车,客户端会把文本发送给服务端。
示例: hello server
客户端发送后,服务端会打印收到的内容,并回复当前在线客户端数量。客户端会显示类似:
Server replied: Received: hello server,当前在线客户端数:1 Server replied: TYPE:DATA_ACK|SEQ:1|BODY:OK
Received DATA_ACK for SEQ:1
3. 退出客户端
客户端输入以下任意命令可主动退出: quit exit
4. 多客户端连接
可以打开多个终端分别运行:
./tcp_client
每有一个客户端接入,服务端会记录客户端的 fd、IP、端口、连接时间,并打印当前在线客户端列表。
客户端发送消息后,服务端回复中会包含当前在线客户端数量。
5. 心跳和自动重连
客户端会定期发送心跳包: TYPE:HEARTBEAT|SEQ:x
服务端收到后回复: TYPE:HEARTBEAT_ACK|SEQ:x|BODY:OK
如果客户端检测到服务端断开,会先进入倒计时检测阶段,期间每秒尝试重连;如果仍未恢复, 则进入最多 5 次的正式重连流程。连续重连失败后,客户端自动退出。
服务端根据客户端最后活跃时间判断空闲连接, 超过 CLIENT_IDLE_TIMEOUT_SEC 后会关闭该客户端连接。
六、协议说明
1. 数据包格式
每个应用层数据包格式为:
| 2 字节 包头 长度 | payload 包内容 |
长度字段使用网络字节序。
最大 payload 长度为 1024 字节。
服务端和客户端都支持处理 TCP 粘包、半包。
2. 业务数据格式
客户端发送普通业务数据时,优先使用: TYPE:DATA|SEQ:序号|BODY:消息内容
服务端收到后会:
打印消息内容。
回复普通业务响应: Received: 消息内容,当前在线客户端数:X
回复数据确认: TYPE:DATA_ACK|SEQ:序号|BODY:OK
如果输入内容过长,客户端会降级为普通 payload 发送,此时服务端仍会回复业务响应, 但客户端不会等待 DATA_ACK。
3. 心跳格式
客户端发送: TYPE:HEARTBEAT|SEQ:序号
服务端回复: TYPE:HEARTBEAT_ACK|SEQ:序号|BODY:OK
七、重要配置
主要配置位于 include/common.h:
#define PORT 8080 #define BACKLOG 5 #define BUFFER_SIZE 2048 #define MAX_PAYLOAD_LEN 1024 #define HEADER_LEN 2 #define RECV_TIMEOUT_SEC 3 #define SEND_TIMEOUT_SEC 3 #define RETRY_MAX 3 #define RETRY_DELAY_SEC 1 #define HEARTBEAT_INTERVAL_SEC 15 #define HEARTBEAT_ACK_TIMEOUT_SEC 3 #define HEARTBEAT_MAX_RETRIES 3 #define CLIENT_IDLE_TIMEOUT_SEC 60客户端连接服务器地址位于 src/tcp_client.c:
#define SERVER_IP "127.0.0.1" #define SERVER_PORT PORT如需连接其他服务器,修改 SERVER_IP 后重新编译。
八、测试说明
1. 编译测试
执行: make clean make
预期结果:
编译过程无错误。
项目根目录生成 tcp_server 和 tcp_client。
2. 单客户端通信测试
步骤:
启动服务端: ./tcp_server
启动客户端: ./tcp_client
在客户端输入: hello
预期结果:
服务端打印收到的客户端消息。
客户端收到 Received: hello,当前在线客户端数:1。
客户端收到对应的
DATA_ACK。
3. 多客户端在线数量测试
步骤:
保持服务端运行。
打开多个终端,分别运行 ./tcp_client。
在任意客户端输入消息。
预期结果:
服务端打印多个客户端的连接信息。
服务端在线客户端列表数量正确。
客户端收到的服务端响应中,
当前在线客户端数与实际连接数一致。
4. 客户端主动退出测试
步骤:
客户端连接服务端后输入: quit 或 exit
预期结果:
客户端程序退出。
服务端检测到客户端断开,并从在线链表中删除该客户端。
服务端输出剩余在线客户端数量。
5. 心跳测试
步骤:
启动服务端和客户端。
不输入任何业务消息,等待约 15 秒。
预期结果:
客户端输出发送心跳的日志,例如 Sent heartbeat SEQ:x。
服务端输出收到心跳的日志,例如 Receive HEARTBEAT, seq=x。
客户端收到
HEARTBEAT_ACK并清除等待状态。
6. 服务端断开与客户端自动重连测试
步骤:
启动服务端和客户端。
停止服务端程序。
观察客户端输出。
在客户端退出前重新启动服务端。
预期结果:
客户端检测到服务端断开。
客户端进入倒计时检测或重连流程。
服务端恢复后,客户端能重新连接成功。
7. 数据 ACK 重试测试
步骤:
启动服务端和客户端。
客户端发送普通消息。
观察客户端是否收到 DATA_ACK。
预期结果:
正常情况下客户端收到 DATA_ACK 后清除待确认数据。
如果 DATA_ACK 超时,客户端会按配置重试发送。
超过最大重试次数后,客户端断开连接并进入重连流程。
8. 长数据测试
步骤:
启动服务端和客户端。
在客户端输入接近 1024 字节的字符串。
预期结果:
未超过 MAX_PAYLOAD_LEN 时,数据可被发送。
如果超过 TYPE:DATA|SEQ:x|BODY: 格式可承载长度,客户端会降级为普通 payload 发送。
超过 MAX_PAYLOAD_LEN 时,客户端提示输入过长并拒绝发送。
9. 空闲超时测试
步骤:
启动服务端。
使用客户端连接服务端。
停止客户端心跳或模拟客户端无数据发送。
等待超过 CLIENT_IDLE_TIMEOUT_SEC。
预期结果:
服务端关闭空闲超时的客户端连接。
服务端从在线客户端链表中删除该客户端。
九、常见问题
1. make 报错找不到交叉编译器
说明当前环境没有安装 arm-linux-gnueabihf-gcc 或 arm-linux-gcc。
解决方式:
在 ARM 交叉编译环境中安装对应工具链。
或修改 Makefile,使用本机 gcc 进行本地测试。
2. 客户端连接失败
检查:
服务端是否已经启动。
服务端端口是否为 8080。
客户端 SERVER_IP 是否指向正确地址。
防火墙是否阻止了端口访问。
3. bind 失败
如果服务端启动时报 bind 错误,可能是 8080 端口已被占用。 可处理方式:
关闭占用该端口的程序。
修改 include/common.h 中的 PORT 后重新编译。
程序下载地址