From 00e8309f0316988f824882e96366d286c4494a8c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 18 Jul 2017 16:25:31 +0300 Subject: [PATCH] samples: sockets: Add async echo server example Implements asynchronous TCP echo server using non-blocking sockets and poll, with concurrent connections support. Signed-off-by: Paul Sokolovsky --- samples/net/sockets/echo_async/Makefile | 18 +++ samples/net/sockets/echo_async/Makefile.posix | 4 + .../net/sockets/echo_async/prj_qemu_x86.conf | 26 ++++ samples/net/sockets/echo_async/sample.yaml | 8 + samples/net/sockets/echo_async/src/Makefile | 3 + .../net/sockets/echo_async/src/socket_echo.c | 140 ++++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 samples/net/sockets/echo_async/Makefile create mode 100644 samples/net/sockets/echo_async/Makefile.posix create mode 100644 samples/net/sockets/echo_async/prj_qemu_x86.conf create mode 100644 samples/net/sockets/echo_async/sample.yaml create mode 100644 samples/net/sockets/echo_async/src/Makefile create mode 100644 samples/net/sockets/echo_async/src/socket_echo.c diff --git a/samples/net/sockets/echo_async/Makefile b/samples/net/sockets/echo_async/Makefile new file mode 100644 index 00000000000..1b9255fe26d --- /dev/null +++ b/samples/net/sockets/echo_async/Makefile @@ -0,0 +1,18 @@ +# Makefile - asynchronous socket-based echo server + +# +# Copyright (c) 2017 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +BOARD ?= qemu_x86 +CONF_FILE ?= prj_$(BOARD).conf + +include $(ZEPHYR_BASE)/Makefile.inc + +ifeq ($(CONFIG_NET_L2_BLUETOOTH), y) + QEMU_EXTRA_FLAGS = -serial unix:/tmp/bt-server-bredr +else + include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack +endif diff --git a/samples/net/sockets/echo_async/Makefile.posix b/samples/net/sockets/echo_async/Makefile.posix new file mode 100644 index 00000000000..a166f75d2c2 --- /dev/null +++ b/samples/net/sockets/echo_async/Makefile.posix @@ -0,0 +1,4 @@ +# This makefile builds sample for POSIX system, like Linux + +socket_echo: src/socket_echo.c + $(CC) $^ -o $@ diff --git a/samples/net/sockets/echo_async/prj_qemu_x86.conf b/samples/net/sockets/echo_async/prj_qemu_x86.conf new file mode 100644 index 00000000000..2435a9e4058 --- /dev/null +++ b/samples/net/sockets/echo_async/prj_qemu_x86.conf @@ -0,0 +1,26 @@ +# General config +CONFIG_NEWLIB_LIBC=y + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +# Network driver config +CONFIG_NET_SLIP_TAP=y +CONFIG_TEST_RANDOM_GENERATOR=y + +# Without CONFIG_NET_BUF_LOG printf() doesn't work +CONFIG_NET_BUF_LOG=y + +# Network address config +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" + +# Network debug config +#CONFIG_NET_DEBUG_SOCKETS=y +CONFIG_SYS_LOG_NET_LEVEL=2 diff --git a/samples/net/sockets/echo_async/sample.yaml b/samples/net/sockets/echo_async/sample.yaml new file mode 100644 index 00000000000..89ee2378e02 --- /dev/null +++ b/samples/net/sockets/echo_async/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: BSD Sockets API TCP echo server sample using non-blocking sockets + name: socket_echo_async +tests: +- test: + build_only: true + platform_whitelist: qemu_x86 + tags: net diff --git a/samples/net/sockets/echo_async/src/Makefile b/samples/net/sockets/echo_async/src/Makefile new file mode 100644 index 00000000000..073377e184f --- /dev/null +++ b/samples/net/sockets/echo_async/src/Makefile @@ -0,0 +1,3 @@ +include $(ZEPHYR_BASE)/samples/net/common/Makefile.common + +obj-y += socket_echo.o diff --git a/samples/net/sockets/echo_async/src/socket_echo.c b/samples/net/sockets/echo_async/src/socket_echo.c new file mode 100644 index 00000000000..7b8f29ceffa --- /dev/null +++ b/samples/net/sockets/echo_async/src/socket_echo.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifndef __ZEPHYR__ + +#include +#include +#include +#include +#include +#include + +#define init_net() + +#else + +#include +#include +#include +#include + +void init_net(void) +{ + int ret = net_app_init("socket_echo", NET_APP_NEED_IPV4, K_SECONDS(3)); + + if (ret < 0) { + printf("Application init failed\n"); + k_panic(); + } +} + +#endif + +/* Number of simultaneous connections will be minus 1 */ +#define NUM_FDS 4 + +struct pollfd pollfds[NUM_FDS]; +int pollnum; + +static void nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} + +int pollfds_add(int fd) +{ + int i; + if (pollnum < NUM_FDS) { + i = pollnum++; + } else { + for (i = 0; i < NUM_FDS; i++) { + if (pollfds[i].fd < 0) { + goto found; + } + } + + return -1; + } + +found: + pollfds[i].fd = fd; + pollfds[i].events = POLLIN; + + return 0; +} + +void pollfds_del(int fd) +{ + for (int i = 0; i < pollnum; i++) { + if (pollfds[i].fd == fd) { + pollfds[i].fd = -1; + break; + } + } +} + +int main(void) +{ + int serv; + struct sockaddr_in bind_addr; + + init_net(); + + serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); + bind_addr.sin_port = htons(4242); + bind(serv, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); + + nonblock(serv); + pollfds_add(serv); + + listen(serv, 5); + + while (1) { + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + char addr_str[32]; + + poll(pollfds, pollnum, -1); + for (int i = 0; i < pollnum; i++) { + if (!(pollfds[i].revents & POLLIN)) { + continue; + } + int fd = pollfds[i].fd; + if (fd == serv) { + int client = accept(serv, (struct sockaddr *)&client_addr, + &client_addr_len); + inet_ntop(client_addr.sin_family, &client_addr.sin_addr, + addr_str, sizeof(addr_str)); + printf("Connection from %s fd=%d\n", addr_str, client); + if (pollfds_add(client) < 0) { + static char msg[] = "Too many connections\n"; + send(client, msg, sizeof(msg) - 1, 0); + close(client); + } else { + nonblock(client); + } + } else { + char buf[128]; + int len = recv(fd, buf, sizeof(buf), 0); + if (len == 0) { + pollfds_del(fd); + close(fd); + printf("Connection fd=%d closed\n", fd); + } else { + /* We assume this won't be short write, d'oh */ + send(fd, buf, len, 0); + } + } + } + } +}