1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #16739 from benpicco/examples/tcp_echo

examples: add TCP echo server & client from documentation
This commit is contained in:
benpicco 2024-11-12 05:28:07 +00:00 committed by GitHub
commit 1976e68177
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 266 additions and 0 deletions

View File

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

View File

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

View File

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

View File

@ -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 <m.lenders@fu-berlin.de>
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/
#include <stdio.h>
#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 <addr> <port> <data>\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 <port>\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;
}
/** @} */