From fa9371d177fddec88bd22ba0ab36ca0bb1a3cd39 Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 17 Dec 2019 17:02:20 +0100 Subject: [PATCH] examples: add `posix_select` example --- examples/posix_select/Makefile | 32 +++++++ examples/posix_select/Makefile.ci | 27 ++++++ examples/posix_select/README.md | 83 ++++++++++++++++++ examples/posix_select/main.c | 141 ++++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+) create mode 100644 examples/posix_select/Makefile create mode 100644 examples/posix_select/Makefile.ci create mode 100644 examples/posix_select/README.md create mode 100644 examples/posix_select/main.c diff --git a/examples/posix_select/Makefile b/examples/posix_select/Makefile new file mode 100644 index 000000000000..485ce451a83e --- /dev/null +++ b/examples/posix_select/Makefile @@ -0,0 +1,32 @@ +# name of your application +APPLICATION = posix_sockets_example + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules for socket communication via UDP +USEMODULE += gnrc_ipv6_default +# Add stack-specific implementations of sock modules +USEMODULE += gnrc_sock_async +USEMODULE += gnrc_sock_udp +# Add POSIX modules +USEMODULE += posix_select +USEMODULE += posix_sockets +USEMODULE += posix_inet + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/posix_select/Makefile.ci b/examples/posix_select/Makefile.ci new file mode 100644 index 000000000000..981a790ea3a7 --- /dev/null +++ b/examples/posix_select/Makefile.ci @@ -0,0 +1,27 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega328p \ + chronos \ + i-nucleo-lrwan1 \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + wsn430-v1_3b \ + wsn430-v1_4 \ + z1 \ + # diff --git a/examples/posix_select/README.md b/examples/posix_select/README.md new file mode 100644 index 000000000000..5923af2cb870 --- /dev/null +++ b/examples/posix_select/README.md @@ -0,0 +1,83 @@ +examples/posix_sockets +====================== +This application is a showcase for RIOT's POSIX select support. To +keep things simple this application has only one-hop support and +no routing capabilities. + +Usage +===== + +Build, flash and start the application: +```sh +export BOARD=your_board +make +make flash +make term +``` + +The `term` make target starts a terminal emulator for your board. It +connects to a default port so you can interact with the shell, usually +that is `/dev/ttyUSB0`. If your port is named differently, the +`PORT=/dev/yourport` (not to be confused with the UDP port) variable can +be used to override this. + + +Example output +============== + +The application starts 4 UDP servers on a selection of different ports that wait +for input simultaneously: +``` +2019-12-17 16:36:45,559 # RIOT select example application +2019-12-17 16:36:45,561 # Started UDP server at [fe80::14ac:fb65:106b:1115]:1350 +2019-12-17 16:36:45,562 # Started UDP server at [fe80::14ac:fb65:106b:1115]:4973 +2019-12-17 16:36:45,562 # Started UDP server at [fe80::14ac:fb65:106b:1115]:6717 +2019-12-17 16:36:45,562 # Started UDP server at [fe80::14ac:fb65:106b:1115]:9673 +``` + +If you do not see any output you might need to reset the node. Either, by +pressing the hardware reset button on the board or by running +```sh +make reset +``` + +There is no shell in this application. You can use the [`posix_sockets` example] +from another board to send a packet to the node: + +``` +> udp send fe80::14ac:fb65:106b:1115 6717 "Hello World!" +2019-12-17 16:47:01,789 # udp send fe80::14ac:fb65:106b:1115%6 6717 "Hello World!" +2019-12-17 16:47:01,795 # Success: send 12 byte to fe80::14ac:fb65:106b:1115:6717 +``` + +On the board with the `posix_select` example you will see then something like +this: + +``` +2019-12-17 16:47:01,796 # Received data from [fe80::589d:9386:2208:6579]:192: +2019-12-17 16:47:01,796 # Hello World! +``` + +Alternatively, with `native` or if your host also can connect to the board, you +can also use [`netcat`][netcat] to send multiple packets simultaneously. E.g. +when the node is connected to the host via the interface `tapbr0`: + +```sh +echo -ne "Hello World!" | nc -6u "fe80::78b9:ecff:fe96:8279%tapbr0" 4973 & \ + echo -ne "Hello Space!" | nc -6u "fe80::78b9:ecff:fe96:8279%tapbr0" 1350 +killall nc +``` + +This is what the `native` node will then show: + +``` +Received data from [fe80::3ccc:8dff:fe9f:9991]:14279: +Hello World! + +Received data from [fe80::3ccc:8dff:fe9f:9991]:58817: +Hello Space! + +``` + +[`posix_sockets` example]: ../posix_sockets +[netcat]: https://www.unix.com/man-page/Linux/1/netcat/ diff --git a/examples/posix_select/main.c b/examples/posix_select/main.c new file mode 100644 index 000000000000..f78bb0143c7b --- /dev/null +++ b/examples/posix_select/main.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Example application for demonstrating the RIOT's POSIX select() + * implementation + * + * @author Martine Lenders + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "od.h" +#include "net/gnrc/netif.h" +#include "thread.h" + +#define SERVER_BUFFER_SIZE (128U) +#define SERVER_PORTS { 1350U, 4973U, 6717U, 9673U } +#define SERVER_SOCKETS_NUM (4U) + +static char server_buffer[SERVER_BUFFER_SIZE]; +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; + +static int _run_server(void *local_addr) +{ + struct sockaddr_in6 server_addr = { .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT }; + static const uint16_t ports[SERVER_SOCKETS_NUM] = SERVER_PORTS; + int server_sockets[SERVER_SOCKETS_NUM] = { 0 }; + int max_fd = -1; + int ret = 0; + + /* open SERVER_SOCKETS_NUM sockets with respective port */ + for (unsigned i = 0; i < SERVER_SOCKETS_NUM; i++) { + server_sockets[i] = socket(AF_INET6, SOCK_DGRAM, 0); + if (server_sockets[i] < 0) { + puts("error initializing socket"); + ret = 1; + goto end; + } + server_addr.sin6_port = htons(ports[i]); + if (bind(server_sockets[i], + (struct sockaddr *)&server_addr, + sizeof(server_addr)) < 0) { + puts("error binding socket"); + ret = 1; + goto end; + } + printf("Started UDP server at [%s]:%u\n", + inet_ntop(AF_INET6, local_addr, addr_str, sizeof(addr_str)), + ports[i]); + if (max_fd < server_sockets[i]) { + max_fd = server_sockets[i]; + } + } + + while (true) { + fd_set readfds; + + /* add bound sockets to set of file descriptors to read */ + FD_ZERO(&readfds); + for (unsigned i = 0; i < SERVER_SOCKETS_NUM; i++) { + FD_SET(server_sockets[i], &readfds); + } + /* wait for bound sockets to be notified for reading*/ + if (select(max_fd + 1, &readfds, NULL, NULL, NULL) < 0) { + puts("error on select"); + continue; + } + for (unsigned i = 0; i < SERVER_SOCKETS_NUM; i++) { + /* if socket is in set of file descriptors to check for reading */ + if (FD_ISSET(server_sockets[i], &readfds)) { + int res; + struct sockaddr_in6 src; + socklen_t src_len = sizeof(struct sockaddr_in6); + + /* receive data from socket */ + if ((res = recvfrom(server_sockets[i], server_buffer, + sizeof(server_buffer), 0, + (struct sockaddr *)&src, &src_len)) < 0) { + puts("Error on receive"); + } + else if (res == 0) { + puts("Peer did shut down"); + } + else { + printf("Received data from [%s]:%u:\n", + inet_ntop(AF_INET6, &src.sin6_addr, + addr_str, sizeof(addr_str)), + src.sin6_port); + res = ((unsigned)res < SERVER_BUFFER_SIZE) ? res : (res - 1); + /* terminate string */ + server_buffer[res] = '\0'; + printf("%s\n", server_buffer); + } + } + } + } + +end: + /* close all open sockets */ + for (unsigned i = 0; i < SERVER_SOCKETS_NUM; i++) { + if (server_sockets[i] > 0) { + close(server_sockets[i]); + } + } + return ret; +} + +int main(void) +{ + /* TODO: use TBD POSIX API to get link-local address */ + gnrc_netif_t *netif = gnrc_netif_iter(NULL); + ipv6_addr_t addr; + + puts("RIOT select example application"); + + /* get first address on the interface */ + if (gnrc_netif_ipv6_addrs_get(netif, &addr, sizeof(addr)) < 0) { + puts("Unable to get first address of the interface"); + return 1; + } + return _run_server(&addr); +}