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=(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_CONNECTION    5

//线圈地址数据的定义
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 = 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;
}

断网重连机制实测有效

 

 

你可能感兴趣的:(编程)