/* ---------- 创建TCP服务器 ---------- */ static int create_tcp_server(int port) { int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { perror("创建套接字失败"); return -1; } // 设置端口复用 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { close(server_fd); return -1; } struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("绑定端口失败"); close(server_fd); return -1; } if (listen(server_fd, 5) < 0) { perror("监听失败"); close(server_fd); return -1; } return server_fd; } /* ---------- TCP通信线程主函数 ---------- */ void* tcp_thread_func(void* arg) { int port = *(int*)arg; int server_fd = create_tcp_server(port); if (server_fd < 0) return NULL; printf("TCP服务器已启动,监听端口:%d\n", port); while (running) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len); if (client_fd < 0) continue; printf("新的TCP连接来自:%s\n", inet_ntoa(client_addr.sin_addr)); uint8_t buffer[256]; ssize_t len = read(client_fd, buffer, sizeof(buffer)); if (len < 7) { // MBAP头长度 close(client_fd); continue; } printf("打印成功"); // 解析MBAP头 uint16_t trans_id = (buffer[0] << 8) | buffer[1]; uint8_t unit_id = buffer[6]; if (unit_id != SLAVE_ADDR) { close(client_fd); continue; } printf("打印通过"); // 处理请求 uint8_t resp_pdu[256]; int pdu_len; pthread_mutex_lock(&data_mutex); pdu_len = handle_modbus_request(&buffer[7]); // 仅传递请求PDU memcpy(resp_pdu, g_resp_buffer, pdu_len); // 从全局缓冲区复制数据 // 构建TCP响应 uint8_t header[7] = { (uint8_t)(trans_id >> 8), // 事务ID高字节 (uint8_t)(trans_id), // 事务ID低字节 0x00, 0x00, // 协议标识符 (uint8_t)((pdu_len + 1) >> 8), // 长度高字节 (uint8_t)(pdu_len + 1), // 长度低字节 SLAVE_ADDR // 单元ID }; write(client_fd, header, sizeof(header)); write(client_fd, resp_pdu, pdu_len); close(client_fd); } close(server_fd); return NULL;
时间: 2025-07-13 09:25:20 浏览: 4
<think>好的,我现在需要帮助用户理解他们提供的C语言代码,特别是关于创建TCP服务器和处理Modbus TCP通信的部分。首先,我会先通读代码,了解整体结构和功能。
用户提供的代码有两个主要部分:`create_tcp_server`函数和`tcp_thread_func`线程函数。我需要分别解释这两个部分的作用,以及它们如何协同工作。先从`create_tcp_server`开始,这个函数负责创建、配置和启动TCP服务器。代码中使用了socket、setsockopt、bind和listen等系统调用,这些都是网络编程的基础。需要注意这些调用的参数和错误处理。
接下来是`tcp_thread_func`,这个函数作为线程运行,处理客户端的连接请求,接收数据,解析Modbus TCP协议,生成响应并返回。这里涉及到accept、read、write等函数,还有对Modbus MBAP头的处理,需要解释清楚每个步骤的作用,比如事务ID、协议标识符、单元ID等字段的意义。
用户可能对代码中的某些细节有疑问,比如端口复用的设置(SO_REUSEADDR)、结构体sockaddr_in的初始化、网络字节序转换(htons),以及多线程中的互斥锁使用(pthread_mutex_lock)。此外,代码中提到的SLAVE_ADDR和全局变量g_resp_buffer可能需要更多的上下文解释,但用户没有提供这些部分,可能需要指出这些依赖。
在解释过程中,需要注意代码中的错误处理,比如socket创建失败、绑定失败等情况,以及如何处理客户端连接中的异常。同时,Modbus TCP的协议解析部分,特别是MBAP头的长度检查、单元ID的验证,以及响应报文的构造,都是关键点,需要详细说明。
用户可能希望了解这段代码的应用场景,比如是否用于工业控制系统的通信,或者Modbus协议的具体功能。需要简要介绍Modbus TCP的基本结构,说明MBAP头的作用和PDU的处理流程。
另外,代码中的一些潜在问题需要注意,比如在accept后没有对client_fd进行非阻塞设置,可能在大量连接时影响性能;read调用可能没有处理中断或部分读取的情况;write调用没有检查返回值,可能导致响应发送不完整。这些是代码中可以改进的地方,用户可能会关心如何优化或增强健壮性。
最后,总结整个流程,强调TCP服务器的创建步骤和通信处理流程,帮助用户形成完整的理解。确保解释清晰,结构合理,覆盖代码中的关键部分和可能的疑问点。</think>以下是代码的逐步解析,主要实现基于TCP的Modbus通信服务:
$$---\text{TCP服务器创建部分}---$$
1. **创建套接字**
```c
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
```
- 使用`AF_INET`(IPv4)和`SOCK_STREAM`(TCP)创建可靠的双向字节流套接字
- 错误时返回负数并打印`perror`
2. **设置端口复用**
```c
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))
```
- 通过`SO_REUSEADDR`选项允许立即重用处于`TIME_WAIT`状态的端口
- 防止服务器重启时出现"Address already in use"错误
3. **绑定地址**
```c
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // 端口转网络字节序
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr))
```
- `INADDR_ANY`表示接受任意IP的连接请求
- `htons()`确保端口号使用正确的字节序
4. **监听连接**
```c
listen(server_fd, 5)
```
- 设置等待队列最大长度为5
- 此时服务器进入被动监听状态
$$---\text{TCP通信处理流程}---$$
1. **接受连接**
```c
int client_fd = accept(server_fd, ...);
```
- 阻塞等待客户端连接
- 返回新的套接字描述符用于数据收发
2. **读取请求数据**
```c
read(client_fd, buffer, sizeof(buffer))
```
- 接收Modbus TCP协议报文
- 要求至少7字节(MBAP头长度验证)
3. **解析MBAP头**
| 字节偏移 | 字段说明 |
|---------|-------------------|
| 0-1 | 事务标识符 |
| 2-3 | 协议标识符(=0) |
| 4-5 | 数据长度(后续字节)|
| 6 | 单元标识符 |
- 校验`unit_id`是否匹配预设的`SLAVE_ADDR`
- 不匹配则立即关闭连接
4. **处理Modbus请求**
```c
handle_modbus_request(&buffer[7])
```
- 将PDU部分(第7字节之后)交给业务逻辑处理
- 使用互斥锁保护共享数据`pthread_mutex_lock(&data_mutex)`
5. **构造响应报文**
```c
write(client_fd, header, sizeof(header));
write(client_fd, resp_pdu, pdu_len);
```
- 响应头包含原事务ID和新的长度计算
- 响应PDU来自全局缓冲区`g_resp_buffer`
$$---\text{关键设计点}---$$
1. **协议适配**
- 符合Modbus TCP规范(RFC 793)
- MBAP头与PDU的分离处理
2. **线程安全**
- 使用`pthread_mutex_lock`保护共享资源
- 每个连接处理完成后立即关闭
3. **错误恢复**
- 所有系统调用都有错误检查
- 异常连接直接关闭不影响服务
$$---\text{改进建议}---$$
1. 增加`select/poll`实现多路复用
2. 添加心跳机制保持长连接
3. 校验协议标识符字段(应全为0)
4. 使用`recv()`替代`read()`以支持标志位
此代码实现了基本的Modbus TCP服务器框架,适合作为工业通信协议的开发基础。实际部署时需根据具体设备协议扩展功能码处理逻辑。
阅读全文
相关推荐



















