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 <paul.sokolovsky@linaro.org>
This commit is contained in:
Paul Sokolovsky 2017-07-18 16:25:31 +03:00 committed by Jukka Rissanen
commit 00e8309f03
6 changed files with 199 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,4 @@
# This makefile builds sample for POSIX system, like Linux
socket_echo: src/socket_echo.c
$(CC) $^ -o $@

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,3 @@
include $(ZEPHYR_BASE)/samples/net/common/Makefile.common
obj-y += socket_echo.o

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#ifndef __ZEPHYR__
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#define init_net()
#else
#include <sys/fcntl.h>
#include <net/socket.h>
#include <kernel.h>
#include <net/net_app.h>
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);
}
}
}
}
}