9.5 KiB
9.5 KiB
API-libcomm — 通讯抽象层模块
版本: v1.0 日期: 2026-07-08 状态: 已实现(TCP/UDP 完整, UART 待实现)
1. 模块概述
libcomm 是 RTU 通讯装置的通讯抽象层,为上层协议栈(Modbus、IEC104 等)提供统一的 TCP/UDP/UART 通讯接口。
设计思想
- 类型统一:同一套 API 操作 TCP 服务器/客户端、UDP、UART
- 异步事件驱动:基于 epoll 单线程事件循环,非阻塞 I/O
- 回调解耦:接收数据和状态变化通过回调通知上层
- 纯 C 接口:
extern "C"包裹,C/C++ 均可调用
架构
┌──────────────────────────────────────────────┐
│ 协议层(libmodbus / lib60870) │
│ 调用 comm_create / comm_send / recv_cb │
├──────────────────────────────────────────────┤
│ libcomm(纯 C 接口层) │
│ ┌──────────┬──────────┬──────────────────┐ │
│ │ TCP 服务 │ TCP 客户 │ UDP 服务 / 客户 │ │
│ │ 器端 │ 端 │ │ │
│ └──────────┴──────────┴──────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ epoll 事件循环 + fd 管理 + 状态机 │ │
│ └──────────────────────────────────────────┘ │
├──────────────────────────────────────────────┤
│ 实现层(C++: map/mutex + POSIX socket) │
└──────────────────────────────────────────────┘
源文件结构
libcomm/
├── inc/
│ └── myComm.h # 对外 C 接口头文件
└── src/
├── comm_internal.h # 内部共享类型/全局变量/函数声明
├── comm_core.cpp # 核心: create/destroy/run/stop + epoll 事件循环
├── comm_tcp.cpp # TCP 服务器/客户端
├── comm_udp.cpp # UDP 通讯
└── comm_uart.cpp # UART 串口通讯
2. 数据结构
2.1 通讯类型
typedef enum
{
COMM_TYPE_TCP_SERVER = 0, /** TCP 服务器(监听+accept) */
COMM_TYPE_TCP_CLIENT, /** TCP 客户端 */
COMM_TYPE_UDP_SERVER, /** UDP 服务器 */
COMM_TYPE_UDP_CLIENT, /** UDP 客户端 */
COMM_TYPE_UART /** UART 串口 */
} enum_comm_type;
2.2 通讯状态
typedef enum
{
COMM_STATE_DISCONNECTED = 0, /** 未连接 */
COMM_STATE_CONNECTING, /** 连接中 */
COMM_STATE_CONNECTED, /** 已连接 */
COMM_STATE_ERROR /** 错误状态 */
} enum_comm_state;
2.3 参数结构体
/** TCP 参数 */
typedef struct
{
char local_ip[64]; /** 本地 IP(服务器用) */
int local_port; /** 本地端口(服务器用) */
char remote_ip[64]; /** 远端 IP(客户端用) */
int remote_port; /** 远端端口(客户端用) */
} stru_tcp_para;
/** UDP 参数 */
typedef struct
{
char local_ip[64]; /** 本地 IP */
int local_port; /** 本地端口 */
char remote_ip[64]; /** 远端 IP(客户端用) */
int remote_port; /** 远端端口(客户端用) */
} stru_udp_para;
/** UART 参数 */
typedef struct
{
char device[128]; /** 串口设备路径(如 /dev/ttyS0) */
int baudrate; /** 波特率 */
int data_bits; /** 数据位(5/6/7/8) */
int stop_bits; /** 停止位(1/2) */
char parity; /** 校验位('N'/'E'/'O') */
} stru_uart_para;
/** 通讯参数联合体 */
typedef union
{
stru_tcp_para tcp;
stru_udp_para udp;
stru_uart_para uart;
} union_comm_para;
2.4 回调函数类型
/** 接收数据回调 */
typedef void (*comm_recv_cb)(int id, int fd, const unsigned char *data, int len);
/** 状态变化回调 */
typedef void (*comm_state_cb)(int id, int fd, int state);
2.5 错误码
| 常量 | 值 | 说明 |
|---|---|---|
COMM_OK |
0 | 成功 |
COMM_ERR_PARAM |
-1 | 参数错误 |
COMM_ERR_MEM |
-2 | 内存不足 |
COMM_ERR_SOCKET |
-3 | Socket 创建失败 |
COMM_ERR_BIND |
-4 | 绑定失败 |
COMM_ERR_LISTEN |
-5 | 监听失败 |
COMM_ERR_CONNECT |
-6 | 连接失败 |
COMM_ERR_SEND |
-7 | 发送失败 |
COMM_ERR_NOT_FOUND |
-8 | 实例不存在 |
COMM_ERR_STATE |
-9 | 状态错误 |
COMM_ERR_INTERNAL |
-10 | 内部错误 |
3. 接口函数
3.1 comm_create — 创建通讯实例
int comm_create(int type, int debug, const union_comm_para *para);
参数:
type:通讯类型(enum_comm_type)debug:是否打印调试信息(1=打印, 0=静默)para:通讯参数(根据 type 选择对应成员)
返回值:>=0 实例 ID,<0 错误码
使用场景:协议栈初始化时创建对应的通讯通道
3.2 comm_connect — 建立连接
int comm_connect(int id);
行为(按类型):
- TCP 服务器:bind + listen,注册到 epoll
- TCP 客户端:非阻塞 connect,监听 EPOLLOUT 等待完成
- UDP:bind 本地端口
- UART:open 串口设备 + 配置波特率/数据位/停止位/校验
返回值:COMM_OK 成功,<0 错误码
3.3 comm_disconnect — 断开连接
int comm_disconnect(int id);
关闭所有 fd(监听 + 客户端 + 本地),保留参数可重新 connect。
3.4 comm_send — 发送数据
int comm_send(int id, int fd, const unsigned char *data, int len);
参数:
fd:目标 fd(TCP 服务器需指定客户端 fd)data/len:数据与长度
返回值:>=0 实际发送字节数,<0 错误码
注意:UDP 客户端会自动 sendto 到创建时指定的远端地址
3.5 comm_recv_register — 注册接收回调
int comm_recv_register(int id, comm_recv_cb cb);
epoll 检测到可读后,自动调用此回调,传入 (id, fd, data, len)。
3.6 comm_state_register — 注册状态回调
int comm_state_register(int id, comm_state_cb cb);
连接建立/断开/错误时回调,传入 (id, fd, state)。
3.7 comm_destroy — 销毁实例
int comm_destroy(int id);
关闭所有 fd、释放内存。
3.8 comm_run / comm_stop — 事件循环
int comm_run(void); /** 阻塞运行 epoll 事件循环 */
void comm_stop(void); /** 停止事件循环 */
约定:调用者在独立线程中调用 comm_run()。停止时调用 comm_stop()。
4. 使用示例
4.1 TCP 客户端
#include "myComm.h"
static void on_recv(int id, int fd, const unsigned char *data, int len)
{
printf("recv %d bytes on fd=%d\n", len, fd);
}
static void on_state(int id, int fd, int state)
{
if (state == COMM_STATE_CONNECTED)
{
printf("connected!\n");
}
}
void tcp_client_example(void)
{
union_comm_para para;
strcpy(para.tcp.remote_ip, "192.168.1.100");
para.tcp.remote_port = 502;
para.tcp.local_ip[0] = '\0';
para.tcp.local_port = 0;
int id = comm_create(COMM_TYPE_TCP_CLIENT, 1, ¶);
comm_recv_register(id, on_recv);
comm_state_register(id, on_state);
comm_connect(id);
/* 在另一个线程启动事件循环 */
comm_run();
}
4.2 TCP 服务器
void tcp_server_example(void)
{
union_comm_para para;
strcpy(para.tcp.local_ip, "0.0.0.0");
para.tcp.local_port = 502;
int id = comm_create(COMM_TYPE_TCP_SERVER, 1, ¶);
comm_recv_register(id, on_recv);
comm_state_register(id, on_state);
comm_connect(id);
comm_run();
}
4.3 UDP
void udp_example(void)
{
union_comm_para para;
strcpy(para.udp.local_ip, "0.0.0.0");
para.udp.local_port = 2404;
int id = comm_create(COMM_TYPE_UDP_SERVER, 1, ¶);
comm_recv_register(id, on_recv);
comm_connect(id);
comm_run();
}
5. 依赖关系
myBase.h → libcomm
(无其他模块依赖)
libcomm 是阶段 2 的第一个模块,被后续 libmodbus、lib60870 等协议栈依赖。
6. 程序运行流程
comm_create(type, para)
→ 分配 stru_comm_instance,拷贝参数,返回 id
→ 首次调用时初始化 epoll + eventfd
comm_connect(id)
→ TCP_SERVER: socket → bind → listen → epoll_add(listen_fd)
→ TCP_CLIENT: socket → nonblock-connect → epoll_add(fd, EPOLLOUT)
→ UDP: socket → bind → epoll_add(fd)
→ UART: open(device) → tcsetattr → epoll_add(fd)
comm_run() [阻塞,需在独立线程中调用]
→ epoll_wait 循环
→ EPOLLIN (listen_fd) → accept → epoll_add(client_fd) → state_cb
→ EPOLLIN (data_fd) → recv → recv_cb
→ EPOLLOUT → getsockopt(SO_ERROR) → connect 完成 → state_cb
→ EPOLLERR/HUP/RDHUP → close_fd → state_cb(DISCONNECTED)
comm_stop()
→ write(eventfd) → epoll 收到 → g_running = false → 退出循环