Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples: add TCP echo server & client from documentation #16739

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
benpicco marked this conversation as resolved.
Show resolved Hide resolved
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 <[email protected]>
* @author Benjamin Valentin <[email protected]>
*/

#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;
}
/** @} */
Loading