Skip to content

Commit

Permalink
examples/sock_tcp_echo: add TCP echo client / server
Browse files Browse the repository at this point in the history
  • Loading branch information
benpicco committed Nov 11, 2024
1 parent d86738f commit f2d5a4c
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 0 deletions.
41 changes: 41 additions & 0 deletions examples/sock_tcp_echo/Makefile
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions examples/sock_tcp_echo/Makefile.ci
Original file line number Diff line number Diff line change
@@ -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 \
#
20 changes: 20 additions & 0 deletions examples/sock_tcp_echo/README.md
Original file line number Diff line number Diff line change
@@ -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
163 changes: 163 additions & 0 deletions examples/sock_tcp_echo/main.c
Original file line number Diff line number Diff line change
@@ -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;
}
/** @} */

0 comments on commit f2d5a4c

Please sign in to comment.