libmodbus TCP断开重连

最近在项目中需要用到modbus TCP通信协议,Qt自带的modbus只能一对一通信,不能做到1对多,所以选择开元库libmodbus
libmodbus协议介绍中文完整带书签版:https://download.csdn.net/download/u011251940/11341158
下载libmodbus源码包:https://www.libmodbus.org/releases/libmodbus-3.0.6.tar.gz
libmodbus使用手册:https://libmodbus.org/docs/v3.0.6/
libmodbus调试工具:https://download.csdn.net/download/u011251940/11341326
安装嵌入式arm版本
tar -zxvf libmodbus-3.0.6.tar.gz
cd libmodbus-3.0.6
./configure --build=i686 --host=arm-cortexa9-linux-gnueabihf --prefix=/opt/libmodbus/install
make -j8; make install
一、简单写一个libmodbus TCP通信demo测试

#include #include #include #include #include #include #include "modbus/modbus.h" #include "modbus/modbus-tcp.h" #include "modbus/modbus-version.h" //线圈地址数据的定义 const uint16_t UT_BITS_ADDRESS = 0x13; const uint16_t UT_BITS_NB = 0x25; const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B }; //离散量输入寄存器地址数据的定义 const uint16_t UT_INPUT_BITS_ADDRESS = 0xC4; const uint16_t UT_INPUT_BITS_NB = 0x16; const uint8_t UT_INPUT_BITS_TAB[] = { 0xAC, 0xDB, 0x35 }; //读保持寄存器地址数据的定义 const uint16_t UT_REGISTERS_ADDRESS = 0x6B; /* Raise a manual exception when this adress is used for the first byte */ const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C; const uint16_t UT_REGISTERS_NB = 0x3; const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 }; /* If the following value is used, a bad response is sent. It's better to test with a lower value than UT_REGISTERS_NB_POINTS to try to raise a segfault. */ const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2; //输入寄存器地址数据定义 const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x08; const uint16_t UT_INPUT_REGISTERS_NB = 0x1; const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; int main(int argc, char*argv[]) { int socket; modbus_t *ctx; modbus_mapping_t *mb_mapping; int rc; int i; uint8_t *query; int header_length; int data; int address; ctx = modbus_new_tcp("127.0.0.1", 502); query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); header_length = modbus_get_header_length(ctx); modbus_set_debug(ctx, TRUE); //new一个modbus的映射空间 mb_mapping = modbus_mapping_new( UT_BITS_ADDRESS + UT_BITS_NB,//读线圈 UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB,//读离散量输入 UT_REGISTERS_ADDRESS + UT_REGISTERS_NB,//读保持寄存器 UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB); //读输入寄存器 if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; }/** 初始化离散量输入寄存器 **/ modbus_set_bits_from_bytes(mb_mapping->tab_input_bits,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,UT_INPUT_BITS_TAB); /** 初始化输入寄存器 **/ for (i=0; i < UT_INPUT_REGISTERS_NB; i++) { mb_mapping->tab_input_registers[UT_INPUT_REGISTERS_ADDRESS+i] =UT_INPUT_REGISTERS_TAB[i]; } /**初始化读保持寄存器**/ for(i=0; itab_registers[UT_REGISTERS_ADDRESS+i]=UT_REGISTERS_TAB[i]; }socket = modbus_tcp_listen(ctx, 1); //监听端口 modbus_tcp_accept(ctx, &socket); //连接阻塞for (; ; ) { //接收modbus的请求报文 rc = modbus_receive(ctx, query); if (rc == -1) { /* Connection closed by the client or error */ break; } address= (query[header_length+1]<<8) + query[header_length+2]; //地址 data=https://www.it610.com/article/(query[header_length+3]<<8)+query[header_length+4]; //数据 /* 功能码 */ if (query[header_length] == 0x06) { //to do 写保持寄存器数据 } //回复modbus请求的数据 rc = modbus_reply(ctx, query, rc, mb_mapping); if (rc == -1) { break; } }printf("Quit the loop: %s\n", modbus_strerror(errno)); close(socket); modbus_mapping_free(mb_mapping); free(query); modbus_free(ctx); return 0; }

这块代码实测基本可以和客户端连接通信,但是断网重连实现不了。

二、断网重连机制部分
#include #include #include #include #include #include #include "modbus.h" #include "modbus-tcp.h" #include "modbus-version.h"#if defined(_WIN32) #include #else #include #include #include #endif#define NB_CONNECTION5//线圈地址数据的定义 const uint16_t UT_BITS_ADDRESS = 0x13; const uint16_t UT_BITS_NB = 0x25; const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B }; //离散量输入寄存器地址数据的定义 const uint16_t UT_INPUT_BITS_ADDRESS = 0xC4; const uint16_t UT_INPUT_BITS_NB = 0x16; const uint8_t UT_INPUT_BITS_TAB[] = { 0xAC, 0xDB, 0x35 }; //读保持寄存器地址数据的定义 const uint16_t UT_REGISTERS_ADDRESS = 0x6B; /* Raise a manual exception when this adress is used for the first byte */ const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C; const uint16_t UT_REGISTERS_NB = 0x3; const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 }; /* If the following value is used, a bad response is sent. It's better to test with a lower value than UT_REGISTERS_NB_POINTS to try to raise a segfault. */ const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2; //输入寄存器地址数据定义 const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x08; const uint16_t UT_INPUT_REGISTERS_NB = 0x1; const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; modbus_t *ctx = NULL; int server_socket; modbus_mapping_t *mb_mapping; static void close_sigint(int dummy) { close(server_socket); modbus_free(ctx); modbus_mapping_free(mb_mapping); exit(dummy); }int main(void) { int master_socket; int rc; fd_set refset; fd_set rdset; /* Maximum file descriptor number */ int fdmax; ctx = modbus_new_tcp("127.0.0.1", 502); //new一个modbus的映射空间 mb_mapping = modbus_mapping_new( UT_BITS_ADDRESS + UT_BITS_NB,//读线圈 UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB,//读离散量输入 UT_REGISTERS_ADDRESS + UT_REGISTERS_NB,//读保持寄存器 UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB); //读输入寄存器 if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; }/** 初始化离散量输入寄存器 **/ modbus_set_bits_from_bytes(mb_mapping->tab_input_bits,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,UT_INPUT_BITS_TAB); /** 初始化输入寄存器 **/ for (i=0; i < UT_INPUT_REGISTERS_NB; i++) { mb_mapping->tab_input_registers[UT_INPUT_REGISTERS_ADDRESS+i] =UT_INPUT_REGISTERS_TAB[i]; } /**初始化读保持寄存器**/ for(i=0; itab_registers[UT_REGISTERS_ADDRESS+i]=UT_REGISTERS_TAB[i]; }server_socket = modbus_tcp_listen(ctx, NB_CONNECTION); signal(SIGINT, close_sigint); /* Clear the reference set of socket */ FD_ZERO(&refset); /* Add the server socket */ FD_SET(server_socket, &refset); /* Keep track of the max file descriptor */ fdmax = server_socket; for (; ; ) { rdset = refset; if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) { perror("Server select() failure."); close_sigint(1); }/* Run through the existing connections looking for data to be * read */ for (master_socket = 0; master_socket <= fdmax; master_socket++) {if (FD_ISSET(master_socket, &rdset)) { if (master_socket == server_socket) { /* A client is asking a new connection */ socklen_t addrlen; struct sockaddr_in clientaddr; int newfd; /* Handle new connections */ addrlen = sizeof(clientaddr); memset(&clientaddr, 0, sizeof(clientaddr)); newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen); if (newfd == -1) { perror("Server accept() error"); } else { FD_SET(newfd, &refset); if (newfd > fdmax) { /* Keep track of the maximum */ fdmax = newfd; } printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd); } } else { /* An already connected master has sent a new query */ uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; int data = https://www.it610.com/article/0; int address=0; modbus_set_socket(ctx, master_socket); rc = modbus_receive(ctx, query); address =(query[header_length+1]<<8) + query[header_length+2]; if(query[header_length] == 0x06 ) { //to do 功能码 写保持寄存器 }if (rc != -1) { modbus_reply(ctx, query, rc, mb_mapping); } else { /* Connection closed by the client, end of server */ printf("Connection closed on socket %d\n", master_socket); close(master_socket); /* Remove from reference set */ FD_CLR(master_socket, &refset); if (master_socket == fdmax) { fdmax--; } } } } } }return 0; }

断网重连机制实测有效

【libmodbus TCP断开重连】

    推荐阅读