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 <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
b9dc69b38e
commit
7e0d8bcf7a
4 changed files with 184 additions and 36 deletions
|
@ -8,6 +8,13 @@
|
||||||
|
|
||||||
mainmenu "Networking echo-server sample application"
|
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
|
config NET_SAMPLE_IFACE2_MY_IPV6_ADDR
|
||||||
string "My IPv6 address for second interface"
|
string "My IPv6 address for second interface"
|
||||||
help
|
help
|
||||||
|
|
|
@ -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_PEER_IPV6_ADDR="2001:db8::2"
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
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
|
||||||
|
|
|
@ -24,10 +24,14 @@ struct data {
|
||||||
u32_t counter;
|
u32_t counter;
|
||||||
} udp;
|
} udp;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int sock;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int sock;
|
int sock;
|
||||||
char recv_buffer[RECV_BUFFER_SIZE];
|
char recv_buffer[RECV_BUFFER_SIZE];
|
||||||
u32_t counter;
|
u32_t counter;
|
||||||
|
} accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS];
|
||||||
} tcp;
|
} tcp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,23 @@ LOG_MODULE_DECLARE(net_echo_server_sample, LOG_LEVEL_DBG);
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "certificate.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_tcp4(void);
|
||||||
static void process_tcp6(void);
|
static void process_tcp6(void);
|
||||||
|
@ -48,7 +64,8 @@ static ssize_t sendall(int sock, const void *buf, size_t len)
|
||||||
return 0;
|
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)
|
socklen_t bind_addrlen)
|
||||||
{
|
{
|
||||||
int ret;
|
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,
|
data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM,
|
||||||
IPPROTO_TLS_1_2);
|
IPPROTO_TLS_1_2);
|
||||||
#else
|
#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
|
#endif
|
||||||
if (data->tcp.sock < 0) {
|
if (data->tcp.sock < 0) {
|
||||||
LOG_ERR("Failed to create TCP socket (%s): %d", data->proto,
|
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);
|
ret = listen(data->tcp.sock, MAX_CLIENT_QUEUE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("Failed to listen on TCP socket (%s): %d", data->proto,
|
LOG_ERR("Failed to listen on TCP socket (%s): %d",
|
||||||
errno);
|
data->proto, errno);
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_tcp(struct data *data)
|
static void handle_data(void *ptr1, void *ptr2, void *ptr3)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int slot = POINTER_TO_INT(ptr1);
|
||||||
int client;
|
struct data *data = ptr2;
|
||||||
int received;
|
bool *in_use = ptr3;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
struct sockaddr_in client_addr;
|
int received;
|
||||||
socklen_t client_addr_len = sizeof(client_addr);
|
int client;
|
||||||
|
int ret;
|
||||||
|
|
||||||
LOG_INF("Waiting for TCP connection on port %d (%s)...",
|
client = data->tcp.accepted[slot].sock;
|
||||||
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);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
received = recv(client, data->tcp.recv_buffer + offset,
|
received = recv(client,
|
||||||
sizeof(data->tcp.recv_buffer) - offset, 0);
|
data->tcp.accepted[slot].recv_buffer + offset,
|
||||||
|
sizeof(data->tcp.accepted[slot].recv_buffer) - offset,
|
||||||
|
0);
|
||||||
|
|
||||||
if (received == 0) {
|
if (received == 0) {
|
||||||
/* Connection closed */
|
/* Connection closed */
|
||||||
|
@ -141,13 +151,17 @@ static int process_tcp(struct data *data)
|
||||||
/* To prevent fragmentation of the response, reply only if
|
/* To prevent fragmentation of the response, reply only if
|
||||||
* buffer is full or there is no more data to read
|
* buffer is full or there is no more data to read
|
||||||
*/
|
*/
|
||||||
if (offset == sizeof(data->tcp.recv_buffer) ||
|
if (offset == sizeof(data->tcp.accepted[slot].recv_buffer) ||
|
||||||
(recv(client, data->tcp.recv_buffer + offset,
|
(recv(client,
|
||||||
sizeof(data->tcp.recv_buffer) - offset,
|
data->tcp.accepted[slot].recv_buffer + offset,
|
||||||
|
sizeof(data->tcp.accepted[slot].recv_buffer) -
|
||||||
|
offset,
|
||||||
MSG_PEEK | MSG_DONTWAIT) < 0 &&
|
MSG_PEEK | MSG_DONTWAIT) < 0 &&
|
||||||
(errno == EAGAIN || errno == EWOULDBLOCK))) {
|
(errno == EAGAIN || errno == EWOULDBLOCK))) {
|
||||||
#endif
|
#endif
|
||||||
ret = sendall(client, data->tcp.recv_buffer, offset);
|
ret = sendall(client,
|
||||||
|
data->tcp.accepted[slot].recv_buffer,
|
||||||
|
offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("TCP (%s): Failed to send, "
|
LOG_ERR("TCP (%s): Failed to send, "
|
||||||
"closing socket", data->proto);
|
"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",
|
LOG_DBG("TCP (%s): Received and replied with %d bytes",
|
||||||
data->proto, offset);
|
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,
|
LOG_INF("%s TCP: Sent %u packets", data->proto,
|
||||||
data->tcp.counter);
|
data->tcp.accepted[slot].counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
@ -169,9 +183,86 @@ static int process_tcp(struct data *data)
|
||||||
#endif
|
#endif
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
|
*in_use = false;
|
||||||
|
|
||||||
(void)close(client);
|
(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)
|
static void process_tcp4(void)
|
||||||
|
@ -224,6 +315,20 @@ static void process_tcp6(void)
|
||||||
|
|
||||||
void start_tcp(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)) {
|
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
||||||
k_thread_start(tcp6_thread_id);
|
k_thread_start(tcp6_thread_id);
|
||||||
}
|
}
|
||||||
|
@ -235,20 +340,45 @@ void start_tcp(void)
|
||||||
|
|
||||||
void stop_tcp(void)
|
void stop_tcp(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
/* Not very graceful way to close a thread, but as we may be blocked
|
/* Not very graceful way to close a thread, but as we may be blocked
|
||||||
* in accept or recv call it seems to be necessary
|
* in accept or recv call it seems to be necessary
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
||||||
k_thread_abort(tcp6_thread_id);
|
k_thread_abort(tcp6_thread_id);
|
||||||
if (conf.ipv6.tcp.sock > 0) {
|
if (conf.ipv6.tcp.sock >= 0) {
|
||||||
(void)close(conf.ipv6.tcp.sock);
|
(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)) {
|
if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
||||||
k_thread_abort(tcp4_thread_id);
|
k_thread_abort(tcp4_thread_id);
|
||||||
if (conf.ipv4.tcp.sock > 0) {
|
if (conf.ipv4.tcp.sock >= 0) {
|
||||||
(void)close(conf.ipv4.tcp.sock);
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue