Skip to content

Commit

Permalink
examples: add posix_select example
Browse files Browse the repository at this point in the history
  • Loading branch information
miri64 committed Jul 1, 2020
1 parent 6ba6740 commit fa9371d
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 0 deletions.
32 changes: 32 additions & 0 deletions examples/posix_select/Makefile
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions examples/posix_select/Makefile.ci
Original file line number Diff line number Diff line change
@@ -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 \
#
83 changes: 83 additions & 0 deletions examples/posix_select/README.md
Original file line number Diff line number Diff line change
@@ -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/
141 changes: 141 additions & 0 deletions examples/posix_select/main.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* @}
*/

#include <stdbool.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#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);
}

0 comments on commit fa9371d

Please sign in to comment.