From 7e0d8bcf7a7c94e440fde5f1d692039be9398ce4 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 26 Sep 2019 12:45:42 +0300 Subject: [PATCH] samples: net: echo-server: Add support for multiple listeners By default only one listener is enabled, but if user specifies CONFIG_NET_SAMPLE_NUM_HANDLERS with value larger than 1, then multiple threads are created, and each will be able to accept connections. Fixes #19374 Signed-off-by: Jukka Rissanen --- samples/net/sockets/echo_server/Kconfig | 7 + samples/net/sockets/echo_server/prj.conf | 7 + samples/net/sockets/echo_server/src/common.h | 8 +- samples/net/sockets/echo_server/src/tcp.c | 198 +++++++++++++++---- 4 files changed, 184 insertions(+), 36 deletions(-) diff --git a/samples/net/sockets/echo_server/Kconfig b/samples/net/sockets/echo_server/Kconfig index b432c308081..30b4149fa98 100644 --- a/samples/net/sockets/echo_server/Kconfig +++ b/samples/net/sockets/echo_server/Kconfig @@ -8,6 +8,13 @@ mainmenu "Networking echo-server sample application" +config NET_SAMPLE_NUM_HANDLERS + int "How many connections to serve at the same time" + default 1 + help + Each connection is served by a thread which needs + memory. Only increase the value here if really needed. + config NET_SAMPLE_IFACE2_MY_IPV6_ADDR string "My IPv6 address for second interface" help diff --git a/samples/net/sockets/echo_server/prj.conf b/samples/net/sockets/echo_server/prj.conf index 8f70eaad1d8..a39c36f4cfe 100644 --- a/samples/net/sockets/echo_server/prj.conf +++ b/samples/net/sockets/echo_server/prj.conf @@ -41,3 +41,10 @@ CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2" CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2" + +# Number of socket descriptors might need adjusting +# if there are more than 1 handlers defined. +CONFIG_POSIX_MAX_FDS=12 + +# How many client can connect to echo-server simultaneously +CONFIG_NET_SAMPLE_NUM_HANDLERS=1 diff --git a/samples/net/sockets/echo_server/src/common.h b/samples/net/sockets/echo_server/src/common.h index c3fdcde9851..453dd2cf351 100644 --- a/samples/net/sockets/echo_server/src/common.h +++ b/samples/net/sockets/echo_server/src/common.h @@ -26,8 +26,12 @@ struct data { struct { int sock; - char recv_buffer[RECV_BUFFER_SIZE]; - u32_t counter; + + struct { + int sock; + char recv_buffer[RECV_BUFFER_SIZE]; + u32_t counter; + } accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS]; } tcp; }; diff --git a/samples/net/sockets/echo_server/src/tcp.c b/samples/net/sockets/echo_server/src/tcp.c index 5fb27af5b7b..37a293fb61e 100644 --- a/samples/net/sockets/echo_server/src/tcp.c +++ b/samples/net/sockets/echo_server/src/tcp.c @@ -20,7 +20,23 @@ LOG_MODULE_DECLARE(net_echo_server_sample, LOG_LEVEL_DBG); #include "common.h" #include "certificate.h" -#define MAX_CLIENT_QUEUE 1 +#define MAX_CLIENT_QUEUE CONFIG_NET_SAMPLE_NUM_HANDLERS + +#if defined(CONFIG_NET_IPV4) +K_THREAD_STACK_ARRAY_DEFINE(tcp4_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS, + STACK_SIZE); +static struct k_thread tcp4_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +static k_tid_t tcp4_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +static bool tcp4_handler_in_use[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +#endif + +#if defined(CONFIG_NET_IPV6) +K_THREAD_STACK_ARRAY_DEFINE(tcp6_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS, + STACK_SIZE); +static struct k_thread tcp6_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +static k_tid_t tcp6_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +static bool tcp6_handler_in_use[CONFIG_NET_SAMPLE_NUM_HANDLERS]; +#endif static void process_tcp4(void); static void process_tcp6(void); @@ -48,7 +64,8 @@ static ssize_t sendall(int sock, const void *buf, size_t len) return 0; } -static int start_tcp_proto(struct data *data, struct sockaddr *bind_addr, +static int start_tcp_proto(struct data *data, + struct sockaddr *bind_addr, socklen_t bind_addrlen) { int ret; @@ -57,7 +74,8 @@ static int start_tcp_proto(struct data *data, struct sockaddr *bind_addr, data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TLS_1_2); #else - data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TCP); + data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, + IPPROTO_TCP); #endif if (data->tcp.sock < 0) { LOG_ERR("Failed to create TCP socket (%s): %d", data->proto, @@ -88,39 +106,31 @@ static int start_tcp_proto(struct data *data, struct sockaddr *bind_addr, ret = listen(data->tcp.sock, MAX_CLIENT_QUEUE); if (ret < 0) { - LOG_ERR("Failed to listen on TCP socket (%s): %d", data->proto, - errno); + LOG_ERR("Failed to listen on TCP socket (%s): %d", + data->proto, errno); ret = -errno; } return ret; } -static int process_tcp(struct data *data) +static void handle_data(void *ptr1, void *ptr2, void *ptr3) { - int ret = 0; - int client; - int received; + int slot = POINTER_TO_INT(ptr1); + struct data *data = ptr2; + bool *in_use = ptr3; int offset = 0; - struct sockaddr_in client_addr; - socklen_t client_addr_len = sizeof(client_addr); + int received; + int client; + int ret; - LOG_INF("Waiting for TCP connection on port %d (%s)...", - MY_PORT, data->proto); - - client = accept(data->tcp.sock, (struct sockaddr *)&client_addr, - &client_addr_len); - if (client < 0) { - LOG_ERR("Error in accept (%s): %d - stopping server", - data->proto, errno); - return -errno; - } - - LOG_INF("TCP (%s): Accepted connection", data->proto); + client = data->tcp.accepted[slot].sock; do { - received = recv(client, data->tcp.recv_buffer + offset, - sizeof(data->tcp.recv_buffer) - offset, 0); + received = recv(client, + data->tcp.accepted[slot].recv_buffer + offset, + sizeof(data->tcp.accepted[slot].recv_buffer) - offset, + 0); if (received == 0) { /* Connection closed */ @@ -141,13 +151,17 @@ static int process_tcp(struct data *data) /* To prevent fragmentation of the response, reply only if * buffer is full or there is no more data to read */ - if (offset == sizeof(data->tcp.recv_buffer) || - (recv(client, data->tcp.recv_buffer + offset, - sizeof(data->tcp.recv_buffer) - offset, + if (offset == sizeof(data->tcp.accepted[slot].recv_buffer) || + (recv(client, + data->tcp.accepted[slot].recv_buffer + offset, + sizeof(data->tcp.accepted[slot].recv_buffer) - + offset, MSG_PEEK | MSG_DONTWAIT) < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) { #endif - ret = sendall(client, data->tcp.recv_buffer, offset); + ret = sendall(client, + data->tcp.accepted[slot].recv_buffer, + offset); if (ret < 0) { LOG_ERR("TCP (%s): Failed to send, " "closing socket", data->proto); @@ -158,9 +172,9 @@ static int process_tcp(struct data *data) LOG_DBG("TCP (%s): Received and replied with %d bytes", data->proto, offset); - if (++data->tcp.counter % 1000 == 0U) { + if (++data->tcp.accepted[slot].counter % 1000 == 0U) { LOG_INF("%s TCP: Sent %u packets", data->proto, - data->tcp.counter); + data->tcp.accepted[slot].counter); } offset = 0; @@ -169,9 +183,86 @@ static int process_tcp(struct data *data) #endif } while (true); + *in_use = false; + (void)close(client); - return ret; + data->tcp.accepted[slot].sock = -1; +} + +static int get_free_slot(struct data *data) +{ + int i; + + for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { + if (data->tcp.accepted[i].sock < 0) { + return i; + } + } + + return -1; +} + +static int process_tcp(struct data *data) +{ + int client; + int slot; + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + + LOG_INF("Waiting for TCP connection on port %d (%s)...", + MY_PORT, data->proto); + + client = accept(data->tcp.sock, (struct sockaddr *)&client_addr, + &client_addr_len); + if (client < 0) { + LOG_ERR("Error in accept (%s): %d - stopping server", + data->proto, -errno); + return -errno; + } + + slot = get_free_slot(data); + if (slot < 0) { + LOG_ERR("Cannot accept more connections"); + close(client); + return 0; + } + + data->tcp.accepted[slot].sock = client; + + LOG_INF("TCP (%s): Accepted connection", data->proto); + +#if defined(CONFIG_NET_IPV6) + if (client_addr.sin_family == AF_INET6) { + tcp6_handler_in_use[slot] = true; + + tcp6_handler_tid[slot] = k_thread_create( + &tcp6_handler_thread[slot], + tcp6_handler_stack[slot], + K_THREAD_STACK_SIZEOF(tcp6_handler_stack[slot]), + (k_thread_entry_t)handle_data, + INT_TO_POINTER(slot), data, &tcp6_handler_in_use[slot], + THREAD_PRIORITY, + 0, K_NO_WAIT); + } +#endif + +#if defined(CONFIG_NET_IPV4) + if (client_addr.sin_family == AF_INET) { + tcp4_handler_in_use[slot] = true; + + tcp4_handler_tid[slot] = k_thread_create( + &tcp4_handler_thread[slot], + tcp4_handler_stack[slot], + K_THREAD_STACK_SIZEOF(tcp4_handler_stack[slot]), + (k_thread_entry_t)handle_data, + INT_TO_POINTER(slot), data, &tcp4_handler_in_use[slot], + THREAD_PRIORITY, + 0, K_NO_WAIT); + } +#endif + + return 0; } static void process_tcp4(void) @@ -224,6 +315,20 @@ static void process_tcp6(void) void start_tcp(void) { + int i; + + for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { + conf.ipv6.tcp.accepted[i].sock = -1; + conf.ipv4.tcp.accepted[i].sock = -1; + +#if defined(CONFIG_NET_IPV4) + tcp4_handler_in_use[i] = false; +#endif +#if defined(CONFIG_NET_IPV6) + tcp4_handler_in_use[i] = false; +#endif + } + if (IS_ENABLED(CONFIG_NET_IPV6)) { k_thread_start(tcp6_thread_id); } @@ -235,20 +340,45 @@ void start_tcp(void) void stop_tcp(void) { + int i; + /* Not very graceful way to close a thread, but as we may be blocked * in accept or recv call it seems to be necessary */ + if (IS_ENABLED(CONFIG_NET_IPV6)) { k_thread_abort(tcp6_thread_id); - if (conf.ipv6.tcp.sock > 0) { + if (conf.ipv6.tcp.sock >= 0) { (void)close(conf.ipv6.tcp.sock); } + + for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { +#if defined(CONFIG_NET_IPV6) + if (tcp6_handler_in_use[i] == true) { + k_thread_abort(tcp6_handler_tid[i]); + } +#endif + if (conf.ipv6.tcp.accepted[i].sock >= 0) { + (void)close(conf.ipv6.tcp.accepted[i].sock); + } + } } if (IS_ENABLED(CONFIG_NET_IPV4)) { k_thread_abort(tcp4_thread_id); - if (conf.ipv4.tcp.sock > 0) { + if (conf.ipv4.tcp.sock >= 0) { (void)close(conf.ipv4.tcp.sock); } + + for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { +#if defined(CONFIG_NET_IPV4) + if (tcp4_handler_in_use[i] == true) { + k_thread_abort(tcp4_handler_tid[i]); + } +#endif + if (conf.ipv4.tcp.accepted[i].sock >= 0) { + (void)close(conf.ipv4.tcp.accepted[i].sock); + } + } } }