From f2d5a4cad9dacef301dd693de26cc982724e8a50 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sun, 15 Aug 2021 12:37:56 +0200 Subject: [PATCH] examples/sock_tcp_echo: add TCP echo client / server --- examples/sock_tcp_echo/Makefile | 41 ++++++++ examples/sock_tcp_echo/Makefile.ci | 42 ++++++++ examples/sock_tcp_echo/README.md | 20 ++++ examples/sock_tcp_echo/main.c | 163 +++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 examples/sock_tcp_echo/Makefile create mode 100644 examples/sock_tcp_echo/Makefile.ci create mode 100644 examples/sock_tcp_echo/README.md create mode 100644 examples/sock_tcp_echo/main.c diff --git a/examples/sock_tcp_echo/Makefile b/examples/sock_tcp_echo/Makefile new file mode 100644 index 0000000000..d809766ade --- /dev/null +++ b/examples/sock_tcp_echo/Makefile @@ -0,0 +1,41 @@ +# name of your application +APPLICATION = sock_tcp_echo + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# default to using GNRC +LWIP ?= 0 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +USEMODULE += sock_tcp +USEMODULE += netdev_default + +USEMODULE += shell +USEMODULE += shell_cmds_default +USEMODULE += ps +USEMODULE += netutils + +ifeq (1, $(LWIP)) + USEMODULE += ipv6_addr + USEMODULE += lwip_ipv6_autoconfig + USEMODULE += lwip_netdev +else + USEMODULE += auto_init_gnrc_netif + USEMODULE += gnrc_ipv6_default + # we want to be able to open two sockets + CFLAGS += -DCONFIG_GNRC_TCP_RCV_BUFFERS=2 +endif + +# 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 + +# As there is a .config we want to explicitly disable Kconfig by setting +# the variable to empty +SHOULD_RUN_KCONFIG ?= + +include $(RIOTBASE)/Makefile.include diff --git a/examples/sock_tcp_echo/Makefile.ci b/examples/sock_tcp_echo/Makefile.ci new file mode 100644 index 0000000000..7cac88ab9a --- /dev/null +++ b/examples/sock_tcp_echo/Makefile.ci @@ -0,0 +1,42 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega328p \ + atmega328p-xplained-mini \ + atmega8 \ + atxmega-a3bu-xplained \ + bluepill-stm32f030c8 \ + derfmega128 \ + i-nucleo-lrwan1 \ + im880b \ + m1284p \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-c031c6 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + olimex-msp430-h1611 \ + olimex-msp430-h2618 \ + samd10-xmini \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32g0316-disco \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + weact-g030f6 \ + z1 \ + zigduino \ + # diff --git a/examples/sock_tcp_echo/README.md b/examples/sock_tcp_echo/README.md new file mode 100644 index 0000000000..330b11aede --- /dev/null +++ b/examples/sock_tcp_echo/README.md @@ -0,0 +1,20 @@ +TCP Echo Server / Client +======================== + +This is a simple TCP echo server / client that uses the SOCK API. +It can make use of both the GNRC and the LWIP network stack. +The default is GNRC, to chose LWIP set `LWIP=1` when compiling the example. + +## Echo Server + +The echo server will echo back any data that it receives. +To start the echo server on port 12345 run + + listen 12345 + +## Echo Client + +The echo client will connect to a server, send some data and wait for the reply. +To send data with the echo client to a server running on 2001:db8::1 run + + send 2001:db8::1 12345 some data diff --git a/examples/sock_tcp_echo/main.c b/examples/sock_tcp_echo/main.c new file mode 100644 index 0000000000..9c167400de --- /dev/null +++ b/examples/sock_tcp_echo/main.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + * @author Benjamin Valentin + */ + +#include + +#include "net/sock/tcp.h" +#include "net/ipv6/addr.h" +#include "net/utils.h" +#include "shell.h" + +#define SOCK_QUEUE_LEN 1 + +static char _echo_server_stack[THREAD_STACKSIZE_DEFAULT]; + +static int _cmd_tcp_send(int argc, char **argv) +{ + int res; + sock_tcp_t sock; + sock_tcp_ep_t remote; + netif_t *netif; + char buf[128]; + + if (argc < 4) { + printf("usage: %s \n", argv[0]); + return -1; + } + + if (netutils_get_ipv6((ipv6_addr_t *)&remote.addr, + &netif, argv[1]) < 0) { + printf("can't resolve %s\n", argv[1]); + return -1; + } + + remote.family = AF_INET6; + remote.netif = netif ? netif_get_id(netif) : 0; + remote.port = atoi(argv[2]); + + if (remote.port == 0) { + printf("Invalid port: %s\n", argv[2]); + return -1; + } + + if ((res = sock_tcp_connect(&sock, &remote, 0, 0)) < 0) { + printf("Error connecting sock: %s\n", strerror(-res)); + return -1; + } + + if ((res = sock_tcp_write(&sock, argv[3], strlen(argv[3]))) < 0) { + printf("Errored on write: %s\n", strerror(-res)); + goto error; + } + + if ((res = sock_tcp_read(&sock, &buf, sizeof(buf), SOCK_NO_TIMEOUT)) <= 0) { + printf("Disconnected: %s\n", strerror(-res)); + goto error; + } + + buf[res] = 0; + printf("Read: \"%s\"\n", buf); + +error: + sock_tcp_disconnect(&sock); + + return 0; +} +SHELL_COMMAND(send, "send data over TCP", _cmd_tcp_send); + +static void *_run_echo_server(void *ctx) +{ + sock_tcp_t sock_queue[SOCK_QUEUE_LEN]; + char buf[128]; + uint16_t port = (uintptr_t)ctx; + + sock_tcp_ep_t local = SOCK_IPV6_EP_ANY; + sock_tcp_queue_t queue; + local.port = port; + if (sock_tcp_listen(&queue, &local, sock_queue, SOCK_QUEUE_LEN, 0) < 0) { + puts("Error creating listening queue"); + return NULL; + } + printf("Listening on port %u\n", port); + while (1) { + sock_tcp_t *sock; + if (sock_tcp_accept(&queue, &sock, SOCK_NO_TIMEOUT) < 0) { + puts("Error accepting new sock"); + break; + } + + int res = 0; + puts("Reading data"); + while (res >= 0) { + res = sock_tcp_read(sock, &buf, sizeof(buf), SOCK_NO_TIMEOUT); + if (res <= 0) { + printf("Disconnected: %s\n", strerror(-res)); + break; + } + buf[res] = 0; + printf("Read: \"%s\"\n", buf); + if ((res = sock_tcp_write(sock, &buf, res)) < 0) { + printf("Errored on write: %s\n", strerror(-res)); + } + } + sock_tcp_disconnect(sock); + } + sock_tcp_stop_listen(&queue); + return NULL; +} + +static int _cmd_tcp_listen(int argc, char **argv) +{ + static kernel_pid_t pid; + uint16_t port; + + if (argc < 2) { + printf("usage: %s \n", argv[0]); + return -1; + } + + if (pid) { + puts("server already running"); + return -1; + } + + port = atoi(argv[1]); + + if (port == 0) { + printf("invalid port: %s\n", argv[1]); + return -1; + } + + pid = thread_create(_echo_server_stack, sizeof(_echo_server_stack), + THREAD_PRIORITY_MAIN - 1, 0, + _run_echo_server, (void*)(uintptr_t)port, "echo_server"); + return 0; +} +SHELL_COMMAND(listen, "start echo server", _cmd_tcp_listen); + +int main(void) +{ + puts("RIOT TCP client example application"); + + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should be never reached */ + return 0; +} +/** @} */