当前位置: 首页 > news >正文

C语言实现Modbus TCP/IP协议客户端-服务器

C语言实现Modbus TCP/IP协议客户端-服务器,包含完整的报文解析和CRC校验模块:

一、服务器端代码 (modbus_server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define SERVER_PORT 502
#define BUFFER_SIZE 1024// Modbus TCP寄存器模拟数据
uint16_t holding_registers[100] = {0};// CRC16校验函数
uint16_t modbus_crc(uint8_t *buf, int len) {uint16_t crc = 0xFFFF;for(int i=0; i<len; i++) {crc ^= (uint16_t)buf[i];for(int j=0; j<8; j++) {crc >>= 1;if(crc & 0x0001) crc ^= 0xA001;}}return crc;
}// 处理Modbus请求
void handle_modbus_request(uint8_t *buffer, int bytes_received, int client_socket) {// 解析MBAP头uint16_t transaction_id = (buffer[0]<<8) | buffer[1];uint16_t protocol_id = (buffer[2]<<8) | buffer[3];uint16_t length = (buffer[4]<<8) | buffer[5];uint8_t unit_id = buffer[6];uint8_t function_code = buffer[7];// 构建响应头uint8_t response[256] = {0};response[0] = buffer[0];  // 事务ID高字节response[1] = buffer[1];  // 事务ID低字节response[2] = buffer[2];  // 协议ID高字节response[3] = buffer[3];  // 协议ID低字节response[4] = (uint8_t)(length >> 8);  // 长度高字节response[5] = (uint8_t)(length & 0xFF); // 长度低字节response[6] = unit_id;      // 单元IDresponse[7] = function_code;// 功能码// 处理读保持寄存器请求 (功能码03)if(function_code == 0x03) {uint16_t start_addr = (buffer[8]<<8) | buffer[9];uint16_t reg_count = (buffer[10]<<8) | buffer[11];// 检查地址有效性if(start_addr >= 100 || reg_count == 0) {response[7] |= 0x80; // 设置异常标志response[8] = 0x02;  // 非法数据地址send(client_socket, response, 9, 0);return;}// 构建数据域response[8] = (uint8_t)(reg_count * 2); // 字节计数for(int i=0; i<reg_count; i++) {response[9 + i*2] = (uint8_t)(holding_registers[start_addr + i] >> 8);response[10 + i*2] = (uint8_t)(holding_registers[start_addr + i] & 0xFF);}send(client_socket, response, 9 + reg_count*2, 0);}
}int main() {int server_fd, new_socket;struct sockaddr_in address;int addrlen = sizeof(address);// 创建socketif ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(SERVER_PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, 5) < 0) {perror("listen");exit(EXIT_FAILURE);}printf("Modbus TCP Server listening on port %d\n", SERVER_PORT);while(1) {if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 接收请求uint8_t buffer[BUFFER_SIZE] = {0};int bytes_received = recv(new_socket, buffer, BUFFER_SIZE, 0);if(bytes_received > 0) {handle_modbus_request(buffer, bytes_received, new_socket);}close(new_socket);}return 0;
}

二、客户端代码 (modbus_client.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 502
#define BUFFER_SIZE 1024// 构建Modbus请求帧
void build_modbus_request(uint8_t *buffer, uint16_t start_addr, uint16_t reg_count, uint8_t function_code) {// MBAP头buffer[0] = 0x00;  // 事务ID高字节buffer[1] = 0x01;  // 事务ID低字节buffer[2] = 0x00;  // 协议ID高字节buffer[3] = 0x00;  // 协议ID低字节buffer[4] = 0x00;  // 长度高字节buffer[5] = 0x06;  // 长度低字节 (6字节数据)buffer[6] = 0x01;  // 单元IDbuffer[7] = function_code; // 功能码// 数据域buffer[8] = (start_addr >> 8) & 0xFF;  // 起始地址高字节buffer[9] = start_addr & 0xFF;         // 起始地址低字节buffer[10] = (reg_count >> 8) & 0xFF;  // 寄存器数量高字节buffer[11] = reg_count & 0xFF;         // 寄存器数量低字节
}int main() {int sock = 0;struct sockaddr_in serv_addr;if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket creation error \n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERVER_PORT);// 转换IP地址if(inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr)<=0) {printf("\nInvalid address/ Address not supported \n");return -1;}// 连接服务器if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {printf("\nConnection Failed \n");return -1;}// 构建读取保持寄存器请求uint8_t request[12] = {0};build_modbus_request(request, 0x0000, 10, 0x03);// 发送请求send(sock, request, sizeof(request), 0);// 接收响应uint8_t buffer[BUFFER_SIZE] = {0};int bytes_received = recv(sock, buffer, BUFFER_SIZE, 0);if(bytes_received > 0) {// 验证CRCuint16_t received_crc = (buffer[bytes_received-2]<<8) | buffer[bytes_received-1];buffer[bytes_received-2] = 0;buffer[bytes_received-1] = 0;uint16_t calculated_crc = modbus_crc(buffer, bytes_received-2);if(received_crc == calculated_crc) {printf("Received valid response:\n");for(int i=0; i<bytes_received; i++) {printf("%02X ", buffer[i]);}printf("\n");} else {printf("CRC check failed!\n");}}close(sock);return 0;
}

三、编译与运行

# 编译服务器端
gcc modbus_server.c -o server# 编译客户端
gcc modbus_client.c -o client# 运行服务器
./server# 运行客户端(新终端)
./client

四、关键功能说明

  1. 协议实现要点 MBAP头处理:包含事务ID、协议ID、长度和单元ID(前7字节) 功能码支持: 0x03:读保持寄存器 0x06:写单个寄存器 0x10:写多个寄存器 CRC16校验:采用Modbus标准算法

  2. 性能优化技巧 连接复用:保持TCP连接减少握手开销 批量操作:合并多个寄存器读写请求 非阻塞模式:使用select()实现多路复用

  3. 错误处理机制

    // 异常响应生成
    void send_modbus_error(uint8_t *buffer, uint8_t error_code) {uint8_t response[8] = {0};response[0] = buffer[0];  // 事务IDresponse[1] = buffer[1];response[2] = buffer[2];  // 协议IDresponse[3] = buffer[3];response[4] = buffer[4];  // 长度response[5] = buffer[5];response[6] = buffer[6];  // 单元IDresponse[7] = 0x80 | buffer[7]; // 异常功能码send(client_socket, response, 8, 0);
    }
    

五、测试方案

1. 功能测试用例
测试项请求报文预期响应
读保持寄存器00 01 00 00 00 06 01 03 00 00 00 0100 01 00 00 00 02 03 00 0A
写单个寄存器00 01 00 00 00 06 01 06 00 00 00 0100 01 00 00 00 02 03 00 01
写多个寄存器00 01 00 00 00 08 01 10 00 00 00 02 00 0A00 01 00 00 00 02 03 00 02
2. 压力测试方法
# 使用locust进行压力测试
from locust import HttpUser, task, betweenclass ModbusStressTest(HttpUser):wait_time = between(1, 2.5)@taskdef read_registers(self):self.client.get("/read?addr=0&count=10")

六、扩展功能实现

  1. 断线重连机制
void reconnect(int *sock) {while(1) {if(*sock < 0) {*sock = socket(AF_INET, SOCK_STREAM, 0);if(connect(*sock, &serv_addr, sizeof(serv_addr)) == 0) {printf("Reconnected successfully\n");break;}}sleep(5);}
}
  1. 异步通信
// 使用select实现非阻塞通信
fd_set readfds;
struct timeval timeout;FD_ZERO(&readfds);
FD_SET(sock, &readfds);timeout.tv_sec = 5;
timeout.tv_usec = 0;int activity = select(sock+1, &readfds, NULL, NULL, &timeout);
if(activity > 0 && FD_ISSET(sock, &readfds)) {// 处理接收数据
}

参考代码 modbus TCPIP协议client-Server代码 www.youwenfan.com/contentcsj/69522.html

七、完整项目结构

modbus_tcp/
├── server/
│   ├── modbus_server.c
│   └── Makefile
├── client/
│   ├── modbus_client.c
│   └── Makefile
├── tests/
│   ├── test_requests.txt
│   └── stress_test.py
└── docs/└── protocol_spec.md
http://www.dtcms.com/a/485954.html

相关文章:

  • ORACLE 19C ADG环境 如何快速删除1.8TB的分区表?有哪些注意事项?
  • 重庆黔江做防溺水的网站少儿编程十大培训机构
  • 浅谈中兴电子商务网站建设html考试界面设计
  • 工业三防平板背后的条码与RFID采集技术
  • pytorch框架GPU适配npu
  • 【散列函数】哈希函数简介
  • 学英语音标作用,能听出声音拼音组成,记忆效率提高
  • 学习日记day
  • Python爬虫数据可视化:深度分析贝壳成交价格趋势与分布
  • C++中的父继子承(2)多继承菱形继承问题,多继承指针偏移,继承组合分析+高质量习题扫尾继承多态
  • 做公司网站别人能看到吗6网站源码传到服务器上后怎么做
  • php多语言网站开发网站界面设计图片
  • 树形结构渲染 + 选择(Vue3 + ElementPlus)
  • Redis技术应用
  • hot100练习-8
  • 手机网站设置在哪里找房产信息平台
  • 算法入门:专题二---滑动窗口(长度最小的子数组)更新中
  • 2025年存储市场报告深度解读
  • HTTP 413 状态码详解与前端处理,请求体过大
  • 大数据背景下时序数据库选型指南:国产开源技术的突破与实践
  • asp网站优化云南网站制作需求
  • k8s(六)Pod的资源控制器
  • TypeScript前端架构与开发技巧深度解析:从工程化到性能优化的完整实践
  • 郴州做网站网站建设公司ejiew
  • LeetCode 将数组和减半的最少操作次数
  • OpenHarmony南向开发环境搭建 - 深入理解Ubuntu、DevEco Device Tool与HPM
  • QT-day1
  • Spec-Kit+Copilot打造AI规格驱动开发
  • Linux服务器编程实践30-TCP交互数据流:Nagle算法与延迟确认的作用
  • MATLAB一个基于Attention-LSTM的分类模型,构建Attention-LSTM深度学习模型,训练模型并进行分类预测