Modbus TCP 通讯协议实现

一、协议数据单元(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服务器:

  1. Modbus Poll (Windows)
  2. QModMaster (跨平台)
  3. mbpoll (命令行工具)
  4. 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 安全考虑

  1. 访问控制:限制IP白名单
  2. 认证机制:实现简单的认证协议
  3. 数据加密:对敏感数据进行加密传输
  4. 日志记录:记录所有访问和操作日志
  5. 资源限制:限制连接数和请求频率
相关推荐
怀旧,1 小时前
【Linux网络编程】1. 网络基础概念
linux·网络
Java成神之路-1 小时前
ARP、RARP与代理ARP详解
tcp/ip·计算机网络
浩瀚之水_csdn1 小时前
Linux grep 命令完全详解
服务器·数据库·mysql
怀旧,2 小时前
【Linux网络编程】5. 应用层协议 HTTP
linux·网络·http
黄金矿工Kingliu2 小时前
Windows运行VMware蓝屏解决方案及网卡配置
运维·服务器
志栋智能2 小时前
超自动化巡检:量化运维成效的标尺
运维·网络·人工智能·自动化
Mike117.2 小时前
GBase 8c MOT 内存表落地前要先画清楚边界
服务器·数据库
夏日听雨眠2 小时前
Linux(信号,管道,共享内存)
java·服务器·网络
小辰记事本3 小时前
从零读懂RDMA UC Write:单向推送,不求回音
网络·网络协议·rdma