socket examples: add tests for server and client applications

This commit is contained in:
David Cermak
2020-03-17 20:30:33 +01:00
committed by bot
parent 63aa0d6e9c
commit 995ef85e85
33 changed files with 696 additions and 328 deletions
@@ -4,7 +4,8 @@ cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common
$ENV{IDF_PATH}/examples/protocols/sockets/socket_addr_from_stdin)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_client)
@@ -17,16 +17,17 @@ In addition to those tools, simple Python scripts can be found under sockets/scr
### TCP server using netcat
```
nc -l 192.168.0.167 -p 3333
nc -l 192.168.0.167 3333
```
### Python scripts
Script tcpserver.py contains configuration for port number and IP version (IPv4 or IPv6) that has to be altered to match the values used by the application. Example:
Script example_test.py could be used as a counter part to the tcp-client project, ip protocol name (IPv4 or IPv6) shall be stated as argument. Example:
```
IP_VERSION = 'IPv4'
PORT = 3333;
python example_test.py IPv4
```
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported;
please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`.
## Hardware Required
@@ -0,0 +1,125 @@
# This example code is in the Public Domain (or CC0 licensed, at your option.)
# Unless required by applicable law or agreed to in writing, this
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied.
# -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import unicode_literals
from builtins import input
import os
import re
import sys
import netifaces
import socket
from threading import Thread, Event
import ttfw_idf
# ----------- Config ----------
PORT = 3333
INTERFACE = 'eth0'
# -------------------------------
def get_my_ip(type):
for i in netifaces.ifaddresses(INTERFACE)[type]:
return i['addr'].replace("%{}".format(INTERFACE), "")
class TcpServer:
def __init__(self, port, family_addr, persist=False):
self.port = port
self.socket = socket.socket(family_addr, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.settimeout(10.0)
self.shutdown = Event()
self.persist = persist
self.family_addr = family_addr
def __enter__(self):
try:
self.socket.bind(('', self.port))
except socket.error as e:
print("Bind failed:{}".format(e))
raise
self.socket.listen(1)
self.server_thread = Thread(target=self.run_server)
self.server_thread.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.persist:
sock = socket.socket(self.family_addr, socket.SOCK_STREAM)
sock.connect(('localhost', self.port))
sock.sendall(b'Stop', )
sock.close()
self.shutdown.set()
self.shutdown.set()
self.server_thread.join()
self.socket.close()
def run_server(self):
while not self.shutdown.is_set():
try:
conn, address = self.socket.accept() # accept new connection
print("Connection from: {}".format(address))
conn.setblocking(1)
data = conn.recv(1024)
if not data:
return
data = data.decode()
print('Received data: ' + data)
reply = 'OK: ' + data
conn.send(reply.encode())
conn.close()
except socket.error as e:
print("Running server failed:{}".format(e))
raise
if not self.persist:
break
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_socket(env, extra_data):
"""
steps:
1. join AP
2. have the board connect to the server
3. send and receive data
"""
dut1 = env.get_dut("tcp_client", "examples/protocols/sockets/tcp_client", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "tcp_client.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("tcp_client_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("tcp_client_bin_size", bin_size // 1024, dut1.TARGET)
# start test
dut1.start_app()
data = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)
print("Connected with IPv4: {}".format(data[0]))
# test IPv4
with TcpServer(PORT, socket.AF_INET):
dut1.write(get_my_ip(netifaces.AF_INET))
dut1.expect(re.compile(r"OK: Message from ESP32"))
# test IPv6
with TcpServer(PORT, socket.AF_INET6):
dut1.write(get_my_ip(netifaces.AF_INET6))
dut1.expect(re.compile(r"OK: Message from ESP32"))
if __name__ == '__main__':
if sys.argv[1:] and sys.argv[1].startswith("IPv"): # if additional arguments provided:
# Usage: example_test.py <IPv4|IPv6>
family_addr = socket.AF_INET6 if sys.argv[1] == "IPv6" else socket.AF_INET
with TcpServer(PORT, family_addr, persist=True) as s:
print(input("Press Enter stop the server..."))
else:
test_examples_protocol_socket()
@@ -2,6 +2,7 @@ menu "Example Configuration"
choice EXAMPLE_IP_MODE
prompt "IP Version"
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
help
Example can use either IPV4 or IPV6.
@@ -34,4 +35,17 @@ menu "Example Configuration"
help
The remote port to which the client example will connect to.
choice EXAMPLE_SOCKET_IP_INPUT
prompt "Socket example source"
default EXAMPLE_SOCKET_IP_INPUT_STRING
help
Selects the input source of the IP used in the example.
config EXAMPLE_SOCKET_IP_INPUT_STRING
bool "From string"
config EXAMPLE_SOCKET_IP_INPUT_STDIN
bool "From stdin"
endchoice
endmenu
@@ -18,17 +18,17 @@
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#ifdef CONFIG_EXAMPLE_IPV4
#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#else
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif
#define PORT CONFIG_EXAMPLE_PORT
@@ -39,39 +39,38 @@ static const char *payload = "Message from ESP32 ";
static void tcp_client_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
while (1) {
#ifdef CONFIG_EXAMPLE_IPV4
#if defined(CONFIG_EXAMPLE_IPV4)
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
#elif defined(CONFIG_EXAMPLE_IPV6)
struct sockaddr_in6 dest_addr = { 0 };
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
inet6_aton(host_ip, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
struct sockaddr_in6 dest_addr = { 0 };
ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#endif
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", HOST_IP_ADDR, PORT);
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
break;
@@ -94,7 +93,7 @@ static void tcp_client_task(void *pvParameters)
// Data received
else {
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
ESP_LOGI(TAG, "%s", rx_buffer);
}
@@ -0,0 +1 @@
CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y