一、协议数据单元(PDU)格式
Modbus TCP ADU (应用数据单元) 格式:
┌─────────────┬─────────────┬─────────────┬─────────────┐
│ MBAP 头 (7字节) │ 功能码 (1字节) │ 数据 (N字节) │ 校验 (可选) │
└─────────────┴─────────────┴─────────────┴─────────────┘
MBAP 头格式:
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ 事务标识 │ 协议标识 │ 长度字段 │ 单元标识 │ 功能代码 │ 起始地址 │ 数量/数据 │
│ (2字节) │ (2字节) │ (2字节) │ (1字节) │ (1字节) │ (2字节) │ (N字节) │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
二、源码实现
2.1 Modbus TCP 协议定义 (modbus_tcp.h)
c
#ifndef __MODBUS_TCP_H
#define __MODBUS_TCP_H
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#endif
// Modbus TCP 常量定义
#define MODBUS_TCP_PORT 502
#define MODBUS_TCP_PROTOCOL_ID 0x0000
#define MODBUS_TCP_HEADER_SIZE 7
#define MODBUS_TCP_MAX_ADU_SIZE 260
#define MODBUS_TCP_MAX_PDU_SIZE 253
// 功能码定义
#define MODBUS_FC_READ_COILS 0x01
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
#define MODBUS_FC_READ_HOLDING_REGS 0x03
#define MODBUS_FC_READ_INPUT_REGS 0x04
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
#define MODBUS_FC_WRITE_SINGLE_REG 0x06
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
#define MODBUS_FC_WRITE_MULTIPLE_REGS 0x10
#define MODBUS_FC_MASK_WRITE_REG 0x16
#define MODBUS_FC_READ_WRITE_REGS 0x17
// 异常码定义
#define MODBUS_EXCEPTION_ILLEGAL_FUNCTION 0x01
#define MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR 0x02
#define MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE 0x03
#define MODBUS_EXCEPTION_SLAVE_FAILURE 0x04
#define MODBUS_EXCEPTION_ACKNOWLEDGE 0x05
#define MODBUS_EXCEPTION_SLAVE_BUSY 0x06
#define MODBUS_EXCEPTION_MEMORY_PARITY 0x08
// 连接状态
typedef enum {
MODBUS_DISCONNECTED = 0,
MODBUS_CONNECTED,
MODBUS_LISTENING,
MODBUS_ERROR
} ModbusConnectionState;
// Modbus TCP ADU 结构
typedef struct {
uint16_t transaction_id; // 事务标识符
uint16_t protocol_id; // 协议标识符
uint16_t length; // 长度字段
uint8_t unit_id; // 单元标识符
uint8_t function_code; // 功能码
uint8_t data[MODBUS_TCP_MAX_PDU_SIZE]; // 数据域
uint16_t data_length; // 数据长度
} ModbusTCPADU;
// Modbus 寄存器映射结构
typedef struct {
uint16_t holding_regs[65536]; // 保持寄存器 (0x0000-0xFFFF)
uint16_t input_regs[65536]; // 输入寄存器 (0x0000-0xFFFF)
uint8_t coils[65536/8]; // 线圈状态 (0x0000-0xFFFF)
uint8_t discrete_inputs[65536/8]; // 离散输入 (0x0000-0xFFFF)
uint32_t last_update_time; // 最后更新时间
} ModbusRegisterMap;
// Modbus TCP 连接结构
typedef struct {
int socket_fd; // Socket 文件描述符
struct sockaddr_in server_addr; // 服务器地址
ModbusConnectionState state; // 连接状态
uint16_t transaction_counter; // 事务计数器
ModbusRegisterMap registers; // 寄存器映射
char ip_address[16]; // IP地址
uint16_t port; // 端口号
uint8_t unit_id; // 单元ID
int timeout_ms; // 超时时间(毫秒)
} ModbusTCPConnection;
// 回调函数类型定义
typedef void (*ModbusCallback)(ModbusTCPADU *request, ModbusTCPADU *response, void *user_data);
// 服务器配置
typedef struct {
uint16_t port; // 监听端口
uint8_t unit_id; // 默认单元ID
int max_connections; // 最大连接数
int timeout_ms; // 超时时间
ModbusCallback callback; // 回调函数
void *user_data; // 用户数据
ModbusRegisterMap *registers; // 寄存器映射
} ModbusTCPServerConfig;
// 函数声明
int ModbusTCP_Init(void);
void ModbusTCP_Cleanup(void);
// 客户端函数
ModbusTCPConnection* ModbusTCP_Connect(const char *ip, uint16_t port, uint8_t unit_id);
void ModbusTCP_Disconnect(ModbusTCPConnection *conn);
int ModbusTCP_ReadHoldingRegisters(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint16_t *values);
int ModbusTCP_WriteSingleRegister(ModbusTCPConnection *conn, uint16_t addr, uint16_t value);
int ModbusTCP_WriteMultipleRegisters(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint16_t *values);
int ModbusTCP_ReadCoils(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint8_t *coils);
int ModbusTCP_WriteSingleCoil(ModbusTCPConnection *conn, uint16_t addr, uint8_t value);
// 服务器函数
int ModbusTCP_StartServer(ModbusTCPServerConfig *config);
void ModbusTCP_StopServer(int server_socket);
int ModbusTCP_HandleClient(int client_socket, ModbusTCPServerConfig *config);
// 协议处理函数
int ModbusTCP_SendADU(ModbusTCPConnection *conn, ModbusTCPADU *adu);
int ModbusTCP_ReceiveADU(ModbusTCPConnection *conn, ModbusTCPADU *adu);
void ModbusTCP_BuildResponse(ModbusTCPADU *request, ModbusTCPADU *response,
ModbusRegisterMap *registers);
uint16_t ModbusTCP_GetNextTransactionID(void);
// 寄存器操作函数
void ModbusTCP_SetHoldingRegister(ModbusRegisterMap *regs, uint16_t addr, uint16_t value);
uint16_t ModbusTCP_GetHoldingRegister(ModbusRegisterMap *regs, uint16_t addr);
void ModbusTCP_SetCoil(ModbusRegisterMap *regs, uint16_t addr, uint8_t value);
uint8_t ModbusTCP_GetCoil(ModbusRegisterMap *regs, uint16_t addr);
#endif /* __MODBUS_TCP_H */
2.2 Modbus TCP 核心实现 (modbus_tcp.c)
c
#include "modbus_tcp.h"
// 全局变量
static uint16_t g_transaction_id = 0;
static int g_socket_initialized = 0;
// 初始化Socket库 (Windows专用)
int ModbusTCP_Init(void) {
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed!\n");
return -1;
}
#endif
g_socket_initialized = 1;
return 0;
}
void ModbusTCP_Cleanup(void) {
#ifdef _WIN32
WSACleanup();
#endif
g_socket_initialized = 0;
}
// 获取下一个事务ID
uint16_t ModbusTCP_GetNextTransactionID(void) {
return ++g_transaction_id;
}
// 创建TCP连接
ModbusTCPConnection* ModbusTCP_Connect(const char *ip, uint16_t port, uint8_t unit_id) {
ModbusTCPConnection *conn = NULL;
struct sockaddr_in server_addr;
if (!g_socket_initialized) {
if (ModbusTCP_Init() != 0) {
return NULL;
}
}
conn = (ModbusTCPConnection*)calloc(1, sizeof(ModbusTCPConnection));
if (!conn) {
return NULL;
}
// 创建Socket
conn->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (conn->socket_fd < 0) {
free(conn);
return NULL;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
#ifdef _WIN32
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {
#else
if (inet_aton(ip, &server_addr.sin_addr) == 0) {
#endif
close(conn->socket_fd);
free(conn);
return NULL;
}
// 连接服务器
if (connect(conn->socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
close(conn->socket_fd);
free(conn);
return NULL;
}
// 设置超时
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
setsockopt(conn->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
conn->state = MODBUS_CONNECTED;
strcpy(conn->ip_address, ip);
conn->port = port;
conn->unit_id = unit_id;
conn->transaction_counter = 0;
printf("Connected to Modbus TCP server %s:%d\n", ip, port);
return conn;
}
// 断开连接
void ModbusTCP_Disconnect(ModbusTCPConnection *conn) {
if (conn) {
if (conn->socket_fd >= 0) {
close(conn->socket_fd);
}
free(conn);
}
}
// 发送ADU
int ModbusTCP_SendADU(ModbusTCPConnection *conn, ModbusTCPADU *adu) {
uint8_t buffer[MODBUS_TCP_MAX_ADU_SIZE];
int offset = 0;
if (!conn || !adu) {
return -1;
}
// 构建MBAP头
buffer[offset++] = (adu->transaction_id >> 8) & 0xFF;
buffer[offset++] = adu->transaction_id & 0xFF;
buffer[offset++] = (adu->protocol_id >> 8) & 0xFF;
buffer[offset++] = adu->protocol_id & 0xFF;
buffer[offset++] = (adu->length >> 8) & 0xFF;
buffer[offset++] = adu->length & 0xFF;
buffer[offset++] = adu->unit_id;
// 添加功能码
buffer[offset++] = adu->function_code;
// 添加数据
memcpy(&buffer[offset], adu->data, adu->data_length);
offset += adu->data_length;
// 发送数据
int sent = send(conn->socket_fd, (char*)buffer, offset, 0);
if (sent != offset) {
return -1;
}
return sent;
}
// 接收ADU
int ModbusTCP_ReceiveADU(ModbusTCPConnection *conn, ModbusTCPADU *adu) {
uint8_t header[MODBUS_TCP_HEADER_SIZE];
int received;
if (!conn || !adu) {
return -1;
}
// 接收MBAP头
received = recv(conn->socket_fd, (char*)header, MODBUS_TCP_HEADER_SIZE, 0);
if (received != MODBUS_TCP_HEADER_SIZE) {
return -1;
}
// 解析MBAP头
adu->transaction_id = (header[0] << 8) | header[1];
adu->protocol_id = (header[2] << 8) | header[3];
adu->length = (header[4] << 8) | header[5];
adu->unit_id = header[6];
// 接收数据部分
if (adu->length > 0) {
received = recv(conn->socket_fd, (char*)adu->data, adu->length - 1, 0);
if (received != adu->length - 1) {
return -1;
}
}
adu->function_code = adu->data[0];
adu->data_length = adu->length - 1;
return MODBUS_TCP_HEADER_SIZE + adu->data_length;
}
// 读取保持寄存器
int ModbusTCP_ReadHoldingRegisters(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint16_t *values) {
ModbusTCPADU request, response;
int result = -1;
if (!conn || !values || quantity == 0 || quantity > 125) {
return -1;
}
// 构建请求
memset(&request, 0, sizeof(request));
request.transaction_id = ModbusTCP_GetNextTransactionID();
request.protocol_id = MODBUS_TCP_PROTOCOL_ID;
request.length = 6; // UnitID(1) + FunctionCode(1) + StartAddr(2) + Quantity(2)
request.unit_id = conn->unit_id;
request.function_code = MODBUS_FC_READ_HOLDING_REGS;
request.data[0] = (start_addr >> 8) & 0xFF;
request.data[1] = start_addr & 0xFF;
request.data[2] = (quantity >> 8) & 0xFF;
request.data[3] = quantity & 0xFF;
request.data_length = 4;
// 发送请求
if (ModbusTCP_SendADU(conn, &request) < 0) {
return -1;
}
// 接收响应
if (ModbusTCP_ReceiveADU(conn, &response) < 0) {
return -1;
}
// 检查响应
if (response.function_code == MODBUS_FC_READ_HOLDING_REGS) {
int byte_count = response.data[1];
if (byte_count == quantity * 2) {
for (int i = 0; i < quantity; i++) {
values[i] = (response.data[2 + i*2] << 8) | response.data[3 + i*2];
}
result = quantity;
}
} else if (response.function_code & 0x80) {
// 异常响应
printf("Modbus exception: 0x%02X\n", response.data[1]);
result = -response.data[1]; // 返回负的异常码
}
return result;
}
// 写单个寄存器
int ModbusTCP_WriteSingleRegister(ModbusTCPConnection *conn, uint16_t addr, uint16_t value) {
ModbusTCPADU request, response;
int result = -1;
if (!conn) {
return -1;
}
// 构建请求
memset(&request, 0, sizeof(request));
request.transaction_id = ModbusTCP_GetNextTransactionID();
request.protocol_id = MODBUS_TCP_PROTOCOL_ID;
request.length = 6;
request.unit_id = conn->unit_id;
request.function_code = MODBUS_FC_WRITE_SINGLE_REG;
request.data[0] = (addr >> 8) & 0xFF;
request.data[1] = addr & 0xFF;
request.data[2] = (value >> 8) & 0xFF;
request.data[3] = value & 0xFF;
request.data_length = 4;
// 发送请求
if (ModbusTCP_SendADU(conn, &request) < 0) {
return -1;
}
// 接收响应
if (ModbusTCP_ReceiveADU(conn, &response) < 0) {
return -1;
}
// 检查响应
if (response.function_code == MODBUS_FC_WRITE_SINGLE_REG) {
uint16_t resp_addr = (response.data[1] << 8) | response.data[2];
uint16_t resp_value = (response.data[3] << 8) | response.data[4];
if (resp_addr == addr && resp_value == value) {
result = 1; // 成功
}
} else if (response.function_code & 0x80) {
printf("Modbus exception: 0x%02X\n", response.data[1]);
result = -response.data[1];
}
return result;
}
// 写多个寄存器
int ModbusTCP_WriteMultipleRegisters(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint16_t *values) {
ModbusTCPADU request, response;
int result = -1;
if (!conn || !values || quantity == 0 || quantity > 123) {
return -1;
}
// 构建请求
memset(&request, 0, sizeof(request));
request.transaction_id = ModbusTCP_GetNextTransactionID();
request.protocol_id = MODBUS_TCP_PROTOCOL_ID;
request.length = 7 + quantity * 2; // UnitID + FC + Addr + Qty + ByteCount + Data
request.unit_id = conn->unit_id;
request.function_code = MODBUS_FC_WRITE_MULTIPLE_REGS;
int offset = 0;
request.data[offset++] = (start_addr >> 8) & 0xFF;
request.data[offset++] = start_addr & 0xFF;
request.data[offset++] = (quantity >> 8) & 0xFF;
request.data[offset++] = quantity & 0xFF;
request.data[offset++] = quantity * 2; // 字节数
for (int i = 0; i < quantity; i++) {
request.data[offset++] = (values[i] >> 8) & 0xFF;
request.data[offset++] = values[i] & 0xFF;
}
request.data_length = 5 + quantity * 2;
// 发送请求
if (ModbusTCP_SendADU(conn, &request) < 0) {
return -1;
}
// 接收响应
if (ModbusTCP_ReceiveADU(conn, &response) < 0) {
return -1;
}
// 检查响应
if (response.function_code == MODBUS_FC_WRITE_MULTIPLE_REGS) {
uint16_t resp_addr = (response.data[1] << 8) | response.data[2];
uint16_t resp_qty = (response.data[3] << 8) | response.data[4];
if (resp_addr == start_addr && resp_qty == quantity) {
result = quantity;
}
} else if (response.function_code & 0x80) {
printf("Modbus exception: 0x%02X\n", response.data[1]);
result = -response.data[1];
}
return result;
}
// 读取线圈状态
int ModbusTCP_ReadCoils(ModbusTCPConnection *conn, uint16_t start_addr,
uint16_t quantity, uint8_t *coils) {
ModbusTCPADU request, response;
int result = -1;
if (!conn || !coils || quantity == 0 || quantity > 2000) {
return -1;
}
// 构建请求
memset(&request, 0, sizeof(request));
request.transaction_id = ModbusTCP_GetNextTransactionID();
request.protocol_id = MODBUS_TCP_PROTOCOL_ID;
request.length = 6;
request.unit_id = conn->unit_id;
request.function_code = MODBUS_FC_READ_COILS;
request.data[0] = (start_addr >> 8) & 0xFF;
request.data[1] = start_addr & 0xFF;
request.data[2] = (quantity >> 8) & 0xFF;
request.data[3] = quantity & 0xFF;
request.data_length = 4;
// 发送请求
if (ModbusTCP_SendADU(conn, &request) < 0) {
return -1;
}
// 接收响应
if (ModbusTCP_ReceiveADU(conn, &response) < 0) {
return -1;
}
// 检查响应
if (response.function_code == MODBUS_FC_READ_COILS) {
int byte_count = response.data[1];
int coil_bytes = (quantity + 7) / 8;
if (byte_count >= coil_bytes) {
for (int i = 0; i < quantity; i++) {
int byte_index = 2 + (i / 8);
int bit_index = i % 8;
coils[i] = (response.data[byte_index] >> bit_index) & 0x01;
}
result = quantity;
}
} else if (response.function_code & 0x80) {
printf("Modbus exception: 0x%02X\n", response.data[1]);
result = -response.data[1];
}
return result;
}
// 写单个线圈
int ModbusTCP_WriteSingleCoil(ModbusTCPConnection *conn, uint16_t addr, uint8_t value) {
ModbusTCPADU request, response;
int result = -1;
if (!conn) {
return -1;
}
// 构建请求
memset(&request, 0, sizeof(request));
request.transaction_id = ModbusTCP_GetNextTransactionID();
request.protocol_id = MODBUS_TCP_PROTOCOL_ID;
request.length = 6;
request.unit_id = conn->unit_id;
request.function_code = MODBUS_FC_WRITE_SINGLE_COIL;
request.data[0] = (addr >> 8) & 0xFF;
request.data[1] = addr & 0xFF;
request.data[2] = value ? 0xFF : 0x00;
request.data[3] = 0x00;
request.data_length = 4;
// 发送请求
if (ModbusTCP_SendADU(conn, &request) < 0) {
return -1;
}
// 接收响应
if (ModbusTCP_ReceiveADU(conn, &response) < 0) {
return -1;
}
// 检查响应
if (response.function_code == MODBUS_FC_WRITE_SINGLE_COIL) {
uint16_t resp_addr = (response.data[1] << 8) | response.data[2];
uint16_t resp_value = (response.data[3] << 8) | response.data[4];
if (resp_addr == addr && ((resp_value == 0xFF00 && value) || (resp_value == 0x0000 && !value))) {
result = 1;
}
} else if (response.function_code & 0x80) {
printf("Modbus exception: 0x%02X\n", response.data[1]);
result = -response.data[1];
}
return result;
}
2.3 Modbus TCP 服务器 (modbus_server.c)
c
#include "modbus_tcp.h"
#include <pthread.h>
// 服务器线程参数
typedef struct {
int client_socket;
ModbusTCPServerConfig config;
} ServerThreadArgs;
// 处理客户端请求
void* HandleClientThread(void *arg) {
ServerThreadArgs *args = (ServerThreadArgs*)arg;
int client_socket = args->client_socket;
ModbusTCPServerConfig *config = &args->config;
ModbusTCPADU request, response;
printf("Client connected: socket %d\n", client_socket);
while (1) {
// 接收请求
int received = recv(client_socket, (char*)&request, sizeof(request), 0);
if (received <= 0) {
break; // 客户端断开连接
}
// 处理请求
ModbusTCP_BuildResponse(&request, &response, config->registers);
// 发送响应
send(client_socket, (char*)&response, sizeof(response), 0);
}
close(client_socket);
free(args);
printf("Client disconnected: socket %d\n", client_socket);
return NULL;
}
// 启动Modbus TCP服务器
int ModbusTCP_StartServer(ModbusTCPServerConfig *config) {
int server_socket;
struct sockaddr_in server_addr;
pthread_t thread_id;
if (!config) {
return -1;
}
if (!g_socket_initialized) {
if (ModbusTCP_Init() != 0) {
return -1;
}
}
// 创建服务器Socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("socket creation failed");
return -1;
}
// 设置SO_REUSEADDR选项
int opt = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(config->port);
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
close(server_socket);
return -1;
}
// 监听
if (listen(server_socket, config->max_connections) < 0) {
perror("listen failed");
close(server_socket);
return -1;
}
printf("Modbus TCP Server started on port %d\n", config->port);
printf("Waiting for connections...\n");
while (1) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
// 接受连接
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
if (client_socket < 0) {
perror("accept failed");
continue;
}
printf("New connection from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 创建线程处理客户端
ServerThreadArgs *thread_args = (ServerThreadArgs*)malloc(sizeof(ServerThreadArgs));
if (thread_args) {
thread_args->client_socket = client_socket;
memcpy(&thread_args->config, config, sizeof(ModbusTCPServerConfig));
if (pthread_create(&thread_id, NULL, HandleClientThread, thread_args) != 0) {
perror("pthread_create failed");
free(thread_args);
close(client_socket);
} else {
pthread_detach(thread_id); // 分离线程,自动回收资源
}
} else {
close(client_socket);
}
}
close(server_socket);
return server_socket;
}
// 构建响应
void ModbusTCP_BuildResponse(ModbusTCPADU *request, ModbusTCPADU *response,
ModbusRegisterMap *registers) {
if (!request || !response || !registers) {
return;
}
// 复制请求头
memcpy(response, request, sizeof(ModbusTCPADU));
uint16_t start_addr, quantity;
uint8_t *data_ptr = response->data + 1; // 跳过功能码
switch (request->function_code) {
case MODBUS_FC_READ_HOLDING_REGS:
start_addr = (request->data[1] << 8) | request->data[2];
quantity = (request->data[3] << 8) | request->data[4];
if (start_addr + quantity <= 65535 && quantity > 0 && quantity <= 125) {
*data_ptr++ = quantity * 2; // 字节数
for (int i = 0; i < quantity; i++) {
uint16_t value = ModbusTCP_GetHoldingRegister(registers, start_addr + i);
*data_ptr++ = (value >> 8) & 0xFF;
*data_ptr++ = value & 0xFF;
}
response->data_length = 1 + 1 + quantity * 2; // 功能码 + 字节数 + 数据
response->length = 1 + response->data_length; // UnitID + 数据长度
} else {
// 异常响应
response->function_code |= 0x80;
response->data[1] = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR;
response->data_length = 2;
response->length = 1 + response->data_length;
}
break;
case MODBUS_FC_WRITE_SINGLE_REG:
start_addr = (request->data[1] << 8) | request->data[2];
uint16_t value = (request->data[3] << 8) | request->data[4];
if (start_addr <= 65535) {
ModbusTCP_SetHoldingRegister(registers, start_addr, value);
response->data_length = 5; // 功能码 + 地址(2) + 值(2)
response->length = 1 + response->data_length;
} else {
response->function_code |= 0x80;
response->data[1] = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR;
response->data_length = 2;
response->length = 1 + response->data_length;
}
break;
case MODBUS_FC_READ_COILS:
start_addr = (request->data[1] << 8) | request->data[2];
quantity = (request->data[3] << 8) | request->data[4];
if (start_addr + quantity <= 65535 && quantity > 0 && quantity <= 2000) {
int byte_count = (quantity + 7) / 8;
*data_ptr++ = byte_count;
for (int i = 0; i < byte_count; i++) {
uint8_t byte_val = 0;
for (int bit = 0; bit < 8 && (i * 8 + bit) < quantity; bit++) {
if (ModbusTCP_GetCoil(registers, start_addr + i * 8 + bit)) {
byte_val |= (1 << bit);
}
}
*data_ptr++ = byte_val;
}
response->data_length = 1 + 1 + byte_count;
response->length = 1 + response->data_length;
} else {
response->function_code |= 0x80;
response->data[1] = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR;
response->data_length = 2;
response->length = 1 + response->data_length;
}
break;
default:
// 不支持的功能码
response->function_code |= 0x80;
response->data[1] = MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
response->data_length = 2;
response->length = 1 + response->data_length;
break;
}
}
// 寄存器操作函数
void ModbusTCP_SetHoldingRegister(ModbusRegisterMap *regs, uint16_t addr, uint16_t value) {
if (regs && addr < 65536) {
regs->holding_regs[addr] = value;
regs->last_update_time = (uint32_t)time(NULL);
}
}
uint16_t ModbusTCP_GetHoldingRegister(ModbusRegisterMap *regs, uint16_t addr) {
if (regs && addr < 65536) {
return regs->holding_regs[addr];
}
return 0;
}
void ModbusTCP_SetCoil(ModbusRegisterMap *regs, uint16_t addr, uint8_t value) {
if (regs && addr < 65536) {
int byte_index = addr / 8;
int bit_index = addr % 8;
if (value) {
regs->coils[byte_index] |= (1 << bit_index);
} else {
regs->coils[byte_index] &= ~(1 << bit_index);
}
regs->last_update_time = (uint32_t)time(NULL);
}
}
uint8_t ModbusTCP_GetCoil(ModbusRegisterMap *regs, uint16_t addr) {
if (regs && addr < 65536) {
int byte_index = addr / 8;
int bit_index = addr % 8;
return (regs->coils[byte_index] >> bit_index) & 0x01;
}
return 0;
}
2.4 主程序示例 (main.c)
c
#include "modbus_tcp.h"
#include <signal.h>
// 全局变量
static volatile int g_running = 1;
static ModbusRegisterMap g_registers;
// 信号处理函数
void SignalHandler(int sig) {
if (sig == SIGINT || sig == SIGTERM) {
printf("\nReceived signal %d, shutting down...\n", sig);
g_running = 0;
}
}
// 初始化寄存器数据
void InitRegisters(ModbusRegisterMap *regs) {
if (!regs) return;
memset(regs, 0, sizeof(ModbusRegisterMap));
// 初始化保持寄存器
for (int i = 0; i < 10; i++) {
regs->holding_regs[i] = 1000 + i * 100; // 1000, 1100, 1200, ...
}
// 初始化输入寄存器
for (int i = 0; i < 10; i++) {
regs->input_regs[i] = 2000 + i * 50; // 2000, 2050, 2100, ...
}
// 初始化线圈
regs->coils[0] = 0x55; // 01010101
regs->coils[1] = 0xAA; // 10101010
printf("Registers initialized\n");
}
// 客户端测试程序
void RunClientTest(void) {
printf("=== Modbus TCP Client Test ===\n");
ModbusTCPConnection *conn = ModbusTCP_Connect("127.0.0.1", 502, 1);
if (!conn) {
printf("Failed to connect to server\n");
return;
}
// 测试读取保持寄存器
uint16_t read_values[5];
int result = ModbusTCP_ReadHoldingRegisters(conn, 0, 5, read_values);
if (result > 0) {
printf("Read %d holding registers:\n", result);
for (int i = 0; i < result; i++) {
printf(" Register[%d] = %d\n", i, read_values[i]);
}
} else {
printf("Failed to read holding registers, error: %d\n", result);
}
// 测试写单个寄存器
result = ModbusTCP_WriteSingleRegister(conn, 5, 9999);
if (result > 0) {
printf("Write single register success\n");
} else {
printf("Failed to write single register, error: %d\n", result);
}
// 测试写多个寄存器
uint16_t write_values[3] = {1111, 2222, 3333};
result = ModbusTCP_WriteMultipleRegisters(conn, 10, 3, write_values);
if (result > 0) {
printf("Write multiple registers success\n");
} else {
printf("Failed to write multiple registers, error: %d\n", result);
}
// 测试读取线圈
uint8_t coils[8];
result = ModbusTCP_ReadCoils(conn, 0, 8, coils);
if (result > 0) {
printf("Read %d coils:\n", result);
for (int i = 0; i < result; i++) {
printf(" Coil[%d] = %d\n", i, coils[i]);
}
} else {
printf("Failed to read coils, error: %d\n", result);
}
// 测试写单个线圈
result = ModbusTCP_WriteSingleCoil(conn, 0, 1);
if (result > 0) {
printf("Write single coil success\n");
} else {
printf("Failed to write single coil, error: %d\n", result);
}
ModbusTCP_Disconnect(conn);
printf("Client test completed\n");
}
// 服务器程序
void RunServer(void) {
printf("=== Modbus TCP Server ===\n");
InitRegisters(&g_registers);
ModbusTCPServerConfig config;
memset(&config, 0, sizeof(config));
config.port = 502;
config.unit_id = 1;
config.max_connections = 10;
config.timeout_ms = 5000;
config.registers = &g_registers;
// 设置信号处理
signal(SIGINT, SignalHandler);
signal(SIGTERM, SignalHandler);
// 启动服务器
int server_socket = ModbusTCP_StartServer(&config);
if (server_socket < 0) {
printf("Failed to start server\n");
return;
}
printf("Server running. Press Ctrl+C to stop...\n");
while (g_running) {
sleep(1);
// 更新输入寄存器(模拟实时数据)
static uint32_t counter = 0;
g_registers.input_regs[0] = counter++;
g_registers.input_regs[1] = rand() % 1000;
}
ModbusTCP_StopServer(server_socket);
printf("Server stopped\n");
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s [client|server]\n", argv[0]);
printf(" client - Run as Modbus TCP client\n");
printf(" server - Run as Modbus TCP server\n");
return 1;
}
ModbusTCP_Init();
if (strcmp(argv[1], "client") == 0) {
RunClientTest();
} else if (strcmp(argv[1], "server") == 0) {
RunServer();
} else {
printf("Invalid argument: %s\n", argv[1]);
printf("Use 'client' or 'server'\n");
return 1;
}
ModbusTCP_Cleanup();
return 0;
}
三、编译与运行
3.1 Linux 编译命令
bash
# 编译服务器
gcc -o modbus_server main.c modbus_tcp.c modbus_server.c -lpthread
# 编译客户端
gcc -o modbus_client main.c modbus_tcp.c -lpthread
# 运行服务器
./modbus_server server
# 运行客户端(新终端)
./modbus_client client
3.2 Windows 编译命令 (MinGW)
batch
# 编译服务器
gcc -o modbus_server.exe main.c modbus_tcp.c modbus_server.c -lws2_32 -lpthread
# 编译客户端
gcc -o modbus_client.exe main.c modbus_tcp.c -lws2_32 -lpthread
# 运行
modbus_server.exe server
modbus_client.exe client
3.3 测试工具
可以使用以下工具测试Modbus TCP服务器:
- Modbus Poll (Windows)
- QModMaster (跨平台)
- mbpoll (命令行工具)
- Python pymodbus
python
# Python测试脚本
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient('127.0.0.1', port=502)
client.connect()
# 读取保持寄存器
result = client.read_holding_registers(0, 10)
print("Holding Registers:", result.registers)
# 写单个寄存器
client.write_register(5, 8888)
client.close()
参考代码 Modbus TCP 通讯协议程序 www.youwenfan.com/contentcsu/60840.html
四、常见问题
4.1 常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 连接超时 | 防火墙阻止 | 开放502端口 |
| 连接拒绝 | 服务器未启动 | 确认服务器正在运行 |
| 功能码不支持 | 设备不支持 | 检查设备手册 |
| 地址越界 | 寄存器地址错误 | 验证地址范围 |
| 数据格式错误 | 字节序问题 | 确认大端/小端格式 |
4.2 安全考虑
- 访问控制:限制IP白名单
- 认证机制:实现简单的认证协议
- 数据加密:对敏感数据进行加密传输
- 日志记录:记录所有访问和操作日志
- 资源限制:限制连接数和请求频率