Explorar o código

New slave able to manage many connections on uniprocessor architecture

- new functions modbus_slave_init_listen_tcp, modbus_slave_accept_tcp,
  modbus_slave_slave_receive.
- removed printf in modbus.c: 'Connection closed'
- new slave test, bandwith-slave-many-up
- updated build scripts
- updated MIGRATION document file
Stéphane Raimbault %!s(int64=16) %!d(string=hai) anos
pai
achega
c7a6ffb357

+ 24 - 0
MIGRATION

@@ -1,3 +1,27 @@
+=============================
+Migration from the 2.0 series
+=============================
+
+modbus_init_listen_tcp() has been renamed to modbus_slave_listen_tcp() and
+requires a new argument, the maximal number of connections:
+
+int modbus_slave_init_tcp(modbus_param_t *mb_param, int nb_connection);
+
+
+New function modbus_slave_accept_tcp() to etablish a new connection (previously
+in modbus_init_listen_tcp()):
+
+int modbus_slave_accept_tcp(modbus_param_t *mb_param, int *socket);
+
+
+modbus_listen() has been renamed to modbus_slave_receive() and requires a new
+argument, the socket file descriptor to listen on. If the sockfd is -1, the
+internal fd of modbus_param_t is used:
+
+int modbus_slave_receive(modbus_param_t *mb_param, int sockfd,
+                         uint8_t *query, int *query_length);
+
+
 ===================================
 Migration notes from the 1.2 series
 ===================================

+ 62 - 52
modbus/modbus.c

@@ -498,7 +498,7 @@ static int receive_msg(modbus_param_t *mb_param,
                 tv.tv_usec = TIME_OUT_BEGIN_OF_TRAME;
                 state = COMPLETE;
         }
-                
+
         length_to_read = msg_length_computed;
 
         select_ret = 0;
@@ -515,7 +515,6 @@ static int receive_msg(modbus_param_t *mb_param,
                         read_ret = recv(mb_param->fd, p_msg, length_to_read, 0);
 
                 if (read_ret == 0) {
-                        printf("Connection closed\n");
                         return CONNECTION_CLOSED;
                 } else if (read_ret < 0) {
                         /* The only negative possible value is -1 */
@@ -524,7 +523,7 @@ static int receive_msg(modbus_param_t *mb_param,
                         return PORT_SOCKET_FAILURE;
                 }
                         
-                /* Sums bytes received */ 
+                /* Sums bytes received */
                 (*p_msg_length) += read_ret;
 
                 /* Display the hex code of each character received */
@@ -590,6 +589,28 @@ static int receive_msg(modbus_param_t *mb_param,
         return 0;
 }
 
+/* Listens for any query from a modbus master in TCP, requires the socket file
+   descriptor etablished with the master device in argument or -1 to use the
+   internal one of modbus_param_t.
+
+   Returns:
+   - 0 if OK, or a negative error number if the request fails
+   - query, message received
+   - query_length, length in bytes of the message */
+int modbus_slave_receive(modbus_param_t *mb_param, int sockfd,
+                         uint8_t *query, int *query_length)
+{
+        int ret;
+
+        if (sockfd != -1) {
+                mb_param->fd = sockfd;
+        }
+
+        /* The length of the query to receive isn't known. */
+        ret = receive_msg(mb_param, MSG_LENGTH_UNDEFINED, query, query_length);
+        
+        return ret;
+}
 
 /* Receives the response and checks values (and checksum in RTU).
 
@@ -757,6 +778,7 @@ static int response_exception(modbus_param_t *mb_param, sft_t *sft,
 
 /* Manages the received query.
    Analyses the query and constructs a response.
+
    If an error occurs, this function construct the response
    accordingly.
 */
@@ -948,21 +970,6 @@ void modbus_manage_query(modbus_param_t *mb_param, const uint8_t *query,
         modbus_send(mb_param, response, resp_length);
 }
 
-/* Listens any message on a socket or file descriptor.
-   Returns:
-   - 0 if OK, or a negative error number if the request fails
-   - query, message received
-   - query_length, length in bytes of the message */
-int modbus_listen(modbus_param_t *mb_param, uint8_t *query, int *query_length)
-{
-        int ret;
-
-        /* The length of the query to receive isn't known. */
-        ret = receive_msg(mb_param, MSG_LENGTH_UNDEFINED, query, query_length);
-        
-        return ret;
-}
-
 /* Reads IO status */
 static int read_io_status(modbus_param_t *mb_param, int slave, int function,
                           int start_addr, int nb, uint8_t *data_dest)
@@ -1628,10 +1635,6 @@ static int modbus_connect_tcp(modbus_param_t *mb_param)
         int option;
         struct sockaddr_in addr;
 
-        addr.sin_family = AF_INET;
-        addr.sin_port = htons(mb_param->port);
-        addr.sin_addr.s_addr = inet_addr(mb_param->ip);
-
         mb_param->fd = socket(AF_INET, SOCK_STREAM, 0);
         if (mb_param->fd < 0) {
                 return mb_param->fd;
@@ -1661,7 +1664,10 @@ static int modbus_connect_tcp(modbus_param_t *mb_param)
         if (mb_param->debug) {
                 printf("Connecting to %s\n", mb_param->ip);
         }
-        
+
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(mb_param->port);
+        addr.sin_addr.s_addr = inet_addr(mb_param->ip);        
         ret = connect(mb_param->fd, (struct sockaddr *)&addr,
                       sizeof(struct sockaddr_in));
         if (ret < 0) {
@@ -1784,60 +1790,64 @@ void modbus_mapping_free(modbus_mapping_t *mb_mapping)
         free(mb_mapping->tab_input_registers);
 }
 
-/* Listens for any query from a modbus master in TCP */
-int modbus_init_listen_tcp(modbus_param_t *mb_param)
+/* Listens for any query from one or many modbus masters in TCP */
+int modbus_slave_listen_tcp(modbus_param_t *mb_param, int nb_connection)
 {
-        int ret;
         int new_socket;
+        int yes;
         struct sockaddr_in addr;
-        socklen_t addrlen;
-
-        addr.sin_family = AF_INET;
-        /* If the modbus port is < to 1024, we need the setuid root. */
-        addr.sin_port = htons(mb_param->port);
-        addr.sin_addr.s_addr = INADDR_ANY;
-        memset(&(addr.sin_zero), '\0', 8);
 
         new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
         if (new_socket < 0) {
                 perror("socket");
-                exit(1);
-        } else {
-                printf("Socket OK\n");
+                return -1;
         }
 
-        ret = bind(new_socket, (struct sockaddr *)&addr,
-                   sizeof(struct sockaddr_in));
-        if (ret < 0) {
+        yes = 1;
+        if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR,
+                       (char *) &yes, sizeof(yes)) < 0) {
+                perror("setsockopt");
+                close(new_socket);
+                return -1;
+        }
+
+        memset(&addr, 0, sizeof(addr));
+        addr.sin_family = AF_INET;
+        /* If the modbus port is < to 1024, we need the setuid root. */
+        addr.sin_port = htons(mb_param->port);
+        addr.sin_addr.s_addr = INADDR_ANY;
+        if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                 perror("bind");
                 close(new_socket);
-                exit(1);
-        } else {
-                printf("Bind OK\n");
+                return -1;
         }
 
-        ret = listen(new_socket, 1);
-        if (ret != 0) {
+        if (listen(new_socket, nb_connection) < 0) {
                 perror("listen");
                 close(new_socket);
-                exit(1);
-        } else {
-                printf("Listen OK\n");
+                return -1;
         }
 
+        return new_socket;
+}
+
+int modbus_slave_accept_tcp(modbus_param_t *mb_param, int *socket)
+{
+        struct sockaddr_in addr;
+        socklen_t addrlen;
+        
         addrlen = sizeof(struct sockaddr_in);
-        mb_param->fd = accept(new_socket, (struct sockaddr *)&addr, &addrlen);
+        mb_param->fd = accept(*socket, (struct sockaddr *)&addr, &addrlen);
         if (mb_param->fd < 0) {
                 perror("accept");
-                close(new_socket);
-                new_socket = 0;
-                exit(1);
+                close(*socket);
+                *socket = 0;
         } else {
                 printf("The client %s is connected\n", 
                        inet_ntoa(addr.sin_addr));
         }
 
-        return new_socket;
+        return mb_param->fd;
 }
 
 /** Utils **/

+ 18 - 5
modbus/modbus.h

@@ -279,12 +279,25 @@ int modbus_mapping_new(modbus_mapping_t *mb_mapping,
 /* Frees the 4 arrays */
 void modbus_mapping_free(modbus_mapping_t *mb_mapping);
 
-/* Initializes the modbus_param_t structure for a TCP slave (server) */
-int modbus_init_listen_tcp(modbus_param_t *mb_param);
+/* Listens for any query from one or many modbus masters in TCP.
 
-/* Listens for any query from a modbus master in TCP 
-   Not tested in RTU communication. */
-int modbus_listen(modbus_param_t *mb_param, uint8_t *query, int *query_length);
+   Returns: socket
+ */
+int modbus_slave_listen_tcp(modbus_param_t *mb_param, int nb_connection);
+
+/* Waits for a connection */
+int modbus_slave_accept_tcp(modbus_param_t *mb_param, int *socket);
+
+/* Listens for any query from a modbus master in TCP, requires the socket file
+   descriptor etablished with the master device in argument.
+
+   Returns:
+   - 0 if OK, or a negative error number if the request fails
+   - query, message received
+   - query_length, length in bytes of the message 
+*/
+int modbus_slave_receive(modbus_param_t *mb_param, int sockfd,
+                         uint8_t *query, int *query_length);
 
 /* Manages the received query.
    Analyses the query and constructs a response.

+ 10 - 6
tests/Makefile.am

@@ -5,8 +5,9 @@ noinst_PROGRAMS = \
 	random-test-master \
 	unit-test-slave \
 	unit-test-master \
-	bench-bandwidth-slave \
-	bench-bandwidth-master
+	bandwidth-slave-one \
+	bandwidth-slave-many-up \
+	bandwidth-master
 
 common_ldflags = \
 	$(top_builddir)/modbus/libmodbus.la
@@ -23,11 +24,14 @@ unit_test_slave_LDADD = $(common_ldflags)
 unit_test_master_SOURCES = unit-test-master.c unit-test.h
 unit_test_master_LDADD = $(common_ldflags)
 
-bench_bandwidth_slave_SOURCES = bench-bandwidth-slave.c
-bench_bandwidth_slave_LDADD = $(common_ldflags)
+bandwidth_slave_one_SOURCES = bandwidth-slave-one.c
+bandwidth_slave_one_LDADD = $(common_ldflags)
 
-bench_bandwidth_master_SOURCES = bench-bandwidth-master.c
-bench_bandwidth_master_LDADD = $(common_ldflags)
+bandwidth_slave_many_up_SOURCES = bandwidth-slave-many-up.c
+bandwidth_slave_many_up_LDADD = $(common_ldflags)
+
+bandwidth_master_SOURCES = bandwidth-master.c
+bandwidth_master_LDADD = $(common_ldflags)
 
 INCLUDES = -I$(top_srcdir)
 CLEANFILES = *~

+ 8 - 8
tests/bench-bandwidth-master.c → tests/bandwidth-master.c

@@ -33,7 +33,7 @@ uint32_t gettime(void)
 {
         struct timeval tv;
         gettimeofday (&tv, NULL);
-        
+
         return (uint32_t) tv.tv_sec * G_USEC_PER_SEC + tv.tv_usec;
 }
 
@@ -61,7 +61,7 @@ int main(void)
         /* Allocate and initialize the memory to store the status */
         tab_rp_status = (uint8_t *) malloc(MAX_STATUS * sizeof(uint8_t));
         memset(tab_rp_status, 0, MAX_STATUS * sizeof(uint8_t));
-        
+
         /* Allocate and initialize the memory to store the registers */
         tab_rp_registers = (uint16_t *) malloc(MAX_REGISTERS * sizeof(uint16_t));
         memset(tab_rp_registers, 0, MAX_REGISTERS * sizeof(uint16_t));
@@ -78,7 +78,7 @@ int main(void)
 
         rate = (NB_LOOPS * nb_points) * G_USEC_PER_SEC / (end - start);
         printf("Transfert rate in points/seconds:\n");
-        printf("* %'d points/s\n", rate); 
+        printf("* %'d points/s\n", rate);
         printf("\n");
 
         bytes = NB_LOOPS * (nb_points / 8) + ((nb_points % 8) ? 1 : 0);
@@ -88,7 +88,7 @@ int main(void)
         printf("* %.3f ms for %d bytes\n", elapsed, bytes);
         printf("* %'d KiB/s\n", rate);
         printf("\n");
-        
+
         /* TCP: Query and reponse header and values */
         bytes = 12 + 9 + (nb_points / 8) + ((nb_points % 8) ? 1 : 0);
         printf("Values and TCP Modbus overhead:\n");
@@ -111,7 +111,7 @@ int main(void)
 
         rate = (NB_LOOPS * nb_points) * G_USEC_PER_SEC / (end - start);
         printf("Transfert rate in points/seconds:\n");
-        printf("* %'d registers/s\n", rate); 
+        printf("* %'d registers/s\n", rate);
         printf("\n");
 
         bytes = NB_LOOPS * nb_points * sizeof(uint16_t);
@@ -121,7 +121,7 @@ int main(void)
         printf("* %.3f ms for %d bytes\n", elapsed, bytes);
         printf("* %'d KiB/s\n", rate);
         printf("\n");
-        
+
         /* TCP:Query and reponse header and values */
         bytes = 12 + 9 + (nb_points * sizeof(uint16_t));
         printf("Values and TCP Modbus overhead:\n");
@@ -133,11 +133,11 @@ int main(void)
         printf("\n");
 
         /* Free the memory */
-        free(tab_rp_status);                                           
+        free(tab_rp_status);
         free(tab_rp_registers);
 
         /* Close the connection */
         modbus_close(&mb_param);
-        
+
         return 0;
 }

+ 132 - 0
tests/bandwidth-slave-many-up.c

@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2009 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <modbus/modbus.h>
+
+#define NB_CONNECTION 5
+int slave_socket;
+modbus_mapping_t mb_mapping;
+
+static void close_sigint(int dummy)
+{
+        shutdown(slave_socket, SHUT_RDWR);
+        close(slave_socket);
+        modbus_mapping_free(&mb_mapping);
+
+	exit(dummy);
+}
+
+int main(void)
+{
+        int master_socket;
+        modbus_param_t mb_param;
+        int ret;
+        fd_set refset;
+        fd_set rdset;
+
+        /* Maximum file descriptor number */
+        int fdmax;
+
+        modbus_init_tcp(&mb_param, "127.0.0.1", 1502);
+
+        ret = modbus_mapping_new(&mb_mapping,  MAX_STATUS, 0, MAX_REGISTERS, 0);
+        if (ret == FALSE) {
+                printf("Memory allocation failure\n");
+                exit(1);
+        }
+
+        slave_socket = modbus_slave_listen_tcp(&mb_param, NB_CONNECTION);
+
+        signal(SIGINT, close_sigint);
+
+        /* Clear the reference set of socket */
+        FD_ZERO(&refset);
+        /* Add the slave socket */
+        FD_SET(slave_socket, &refset);
+
+        /* Keep track of the max file descriptor */
+        fdmax = slave_socket;
+
+        for (;;) {
+                rdset = refset;
+                if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) {
+                        perror("Slave 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 == slave_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(slave_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[MAX_MESSAGE_LENGTH];
+                                        int query_size;
+
+                                        ret = modbus_slave_receive(&mb_param, master_socket, query, &query_size);
+                                        if (ret == 0) {
+                                                modbus_manage_query(&mb_param, query, query_size, &mb_mapping);
+                                        } else {
+                                                /* Connection closed by the client, end of server */
+                                                printf("Connection closed on socket %d\n", master_socket);
+                                                shutdown(master_socket, SHUT_RDWR);
+                                                close(master_socket);
+
+                                                /* Remove from reference set */
+                                                FD_CLR(master_socket, &refset);
+
+                                                if (master_socket == fdmax) {
+                                                        fdmax--;
+                                                }
+                                        }
+                                }
+                        }
+                }
+        }
+
+        return 0;
+}

+ 4 - 3
tests/bench-bandwidth-slave.c → tests/bandwidth-slave-one.c

@@ -37,13 +37,14 @@ int main(void)
                 exit(1);
         }
 
-        socket = modbus_init_listen_tcp(&mb_param);
-        
+        socket = modbus_slave_listen_tcp(&mb_param, 1);
+        modbus_slave_accept_tcp(&mb_param, &socket);
+
         while (1) {
                 uint8_t query[MAX_MESSAGE_LENGTH];
                 int query_size;
                 
-                ret = modbus_listen(&mb_param, query, &query_size);
+                ret = modbus_slave_receive(&mb_param, -1, query, &query_size);
                 if (ret == 0) {
                         modbus_manage_query(&mb_param, query, query_size, &mb_mapping);
                 } else if (ret == CONNECTION_CLOSED) {

+ 4 - 3
tests/random-test-slave.c

@@ -37,13 +37,14 @@ int main(void)
                 exit(1);
         }
         
-        socket = modbus_init_listen_tcp(&mb_param);
-        
+        socket = modbus_slave_listen_tcp(&mb_param, 1);
+        modbus_slave_accept_tcp(&mb_param, &socket);
+
         while (1) {
                 uint8_t query[MAX_MESSAGE_LENGTH];
                 int query_size;
                 
-                ret = modbus_listen(&mb_param, query, &query_size);
+                ret = modbus_slave_receive(&mb_param, -1, query, &query_size);
                 if (ret == 0) {
                         modbus_manage_query(&mb_param, query, query_size, &mb_mapping);
                 } else if (ret == CONNECTION_CLOSED) {

+ 4 - 3
tests/unit-test-slave.c

@@ -59,13 +59,14 @@ int main(void)
                         UT_INPUT_REGISTERS_TAB[i];;
         }
 
-        socket = modbus_init_listen_tcp(&mb_param);
-        
+        socket = modbus_slave_listen_tcp(&mb_param, 1);
+        modbus_slave_accept_tcp(&mb_param, &socket);
+
         while (1) {
                 uint8_t query[MAX_MESSAGE_LENGTH];
                 int query_size;
                 
-                ret = modbus_listen(&mb_param, query, &query_size);
+                ret = modbus_slave_receive(&mb_param, -1, query, &query_size);
                 if (ret == 0) {
                         if (((query[HEADER_LENGTH_TCP + 4] << 8) + query[HEADER_LENGTH_TCP + 5])
                             == UT_HOLDING_REGISTERS_NB_POINTS_SPECIAL) {

+ 11 - 4
tests/wscript

@@ -28,15 +28,22 @@ def build(bld):
     obj.inst_var = 0
 
     obj = bld.create_obj('cc', 'program')
-    obj.source = 'bench-bandwidth-slave.c'
+    obj.source = 'bandwidth-slave-one.c'
     obj.includes = '. ..'
     obj.uselib_local = 'modbus'
-    obj.target = 'bench-bandwidth-slave'
+    obj.target = 'bandwidth-slave-one'
     obj.inst_var = 0
 
     obj = bld.create_obj('cc', 'program')
-    obj.source = 'bench-bandwidth-master.c'
+    obj.source = 'bandwidth-slave-many-up.c'
     obj.includes = '. ..'
     obj.uselib_local = 'modbus'
-    obj.target = 'bench-bandwidth-master'
+    obj.target = 'bandwidth-slave-many-up'
+    obj.inst_var = 0
+
+    obj = bld.create_obj('cc', 'program')
+    obj.source = 'bandwidth-master.c'
+    obj.includes = '. ..'
+    obj.uselib_local = 'modbus'
+    obj.target = 'bandwidth-master'
     obj.inst_var = 0