mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-05 12:25:03 +02:00
socket examples: add tests for server and client applications
This commit is contained in:
@@ -21,14 +21,14 @@ nc 192.168.0.167 3333
|
||||
```
|
||||
|
||||
### Python scripts
|
||||
Script tcpclient.py contains configuration for port number, IP version (IPv4 or IPv6) and IP address 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-server application,
|
||||
IP address and the message to be send to the server shall be stated as arguments. Example:
|
||||
|
||||
```
|
||||
PORT = 3333;
|
||||
IP_VERSION = 'IPv4'
|
||||
IPV4 = '192.168.0.167'
|
||||
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
|
||||
python example_test.py 192.168.0.167 Message
|
||||
```
|
||||
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,88 @@
|
||||
# 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
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import socket
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
# ----------- Config ----------
|
||||
PORT = 3333
|
||||
INTERFACE = 'eth0'
|
||||
# -------------------------------
|
||||
|
||||
|
||||
def tcp_client(address, payload):
|
||||
for res in socket.getaddrinfo(address, PORT, socket.AF_UNSPEC,
|
||||
socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
|
||||
family_addr, socktype, proto, canonname, addr = res
|
||||
try:
|
||||
sock = socket.socket(family_addr, socket.SOCK_STREAM)
|
||||
except socket.error as msg:
|
||||
print('Could not create socket: ' + str(msg[0]) + ': ' + msg[1])
|
||||
raise
|
||||
try:
|
||||
sock.connect(addr)
|
||||
except socket.error as msg:
|
||||
print('Could not open socket: ', msg)
|
||||
sock.close()
|
||||
raise
|
||||
sock.sendall(payload)
|
||||
data = sock.recv(1024)
|
||||
if not data:
|
||||
return
|
||||
print('Reply : ' + data.decode())
|
||||
sock.close()
|
||||
return data
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
|
||||
def test_examples_protocol_socket(env, extra_data):
|
||||
MESSAGE = "Data to ESP"
|
||||
"""
|
||||
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_server", dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, "tcp_server.bin")
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance("tcp_server_bin_size", "{}KB".format(bin_size // 1024))
|
||||
ttfw_idf.check_performance("tcp_server_bin_size", bin_size // 1024, dut1.TARGET)
|
||||
|
||||
# start test
|
||||
dut1.start_app()
|
||||
|
||||
ipv4 = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)[0]
|
||||
ipv6 = dut1.expect(re.compile(r" IPv6 address: ([0-9A-Fa-f\:]+)"), timeout=30)[0]
|
||||
print("Connected with IPv4={} and IPv6={}".format(ipv4, ipv6))
|
||||
|
||||
# test IPv4
|
||||
received = tcp_client(ipv4, MESSAGE)
|
||||
if not received == MESSAGE:
|
||||
raise
|
||||
dut1.expect(MESSAGE)
|
||||
# test IPv6
|
||||
received = tcp_client("{}%{}".format(ipv6, INTERFACE), MESSAGE)
|
||||
if not received == MESSAGE:
|
||||
raise
|
||||
dut1.expect(MESSAGE)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[2:]: # if two arguments provided:
|
||||
# Usage: example_test.py <server_address> <message_to_send_to_server>
|
||||
tcp_client(sys.argv[1], sys.argv[2])
|
||||
else: # otherwise run standard example test as in the CI
|
||||
test_examples_protocol_socket()
|
||||
@@ -1,18 +1,13 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_IP_MODE
|
||||
prompt "IP Version"
|
||||
help
|
||||
Example can use either IPV4 or IPV6.
|
||||
config EXAMPLE_IPV4
|
||||
bool "IPV4"
|
||||
default y
|
||||
|
||||
config EXAMPLE_IPV4
|
||||
bool "IPV4"
|
||||
|
||||
config EXAMPLE_IPV6
|
||||
bool "IPV6"
|
||||
select EXAMPLE_CONNECT_IPV6
|
||||
|
||||
endchoice
|
||||
config EXAMPLE_IPV6
|
||||
bool "IPV6"
|
||||
default n
|
||||
select EXAMPLE_CONNECT_IPV6
|
||||
|
||||
config EXAMPLE_PORT
|
||||
int "Port"
|
||||
|
||||
@@ -60,27 +60,22 @@ static void do_retransmit(const int sock)
|
||||
static void tcp_server_task(void *pvParameters)
|
||||
{
|
||||
char addr_str[128];
|
||||
int addr_family;
|
||||
int ip_protocol;
|
||||
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
struct sockaddr_in dest_addr;
|
||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
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
|
||||
int addr_family = (int)pvParameters;
|
||||
int ip_protocol = 0;
|
||||
struct sockaddr_in6 dest_addr;
|
||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
||||
dest_addr.sin6_family = AF_INET6;
|
||||
dest_addr.sin6_port = htons(PORT);
|
||||
addr_family = AF_INET6;
|
||||
ip_protocol = IPPROTO_IPV6;
|
||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
#endif
|
||||
|
||||
if (addr_family == AF_INET) {
|
||||
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
|
||||
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
dest_addr_ip4->sin_family = AF_INET;
|
||||
dest_addr_ip4->sin_port = htons(PORT);
|
||||
ip_protocol = IPPROTO_IP;
|
||||
} else if (addr_family == AF_INET6) {
|
||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
||||
dest_addr.sin6_family = AF_INET6;
|
||||
dest_addr.sin6_port = htons(PORT);
|
||||
ip_protocol = IPPROTO_IPV6;
|
||||
}
|
||||
|
||||
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
|
||||
if (listen_sock < 0) {
|
||||
@@ -88,11 +83,20 @@ static void tcp_server_task(void *pvParameters)
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
|
||||
// Note that by default IPV6 binds to both protocols, it is must be disabled
|
||||
// if both protocols used at the same time (used in CI)
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
|
||||
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
|
||||
goto CLEAN_UP;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
|
||||
@@ -146,5 +150,10 @@ void app_main(void)
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_EXAMPLE_IPV4=y
|
||||
CONFIG_EXAMPLE_IPV6=y
|
||||
Reference in New Issue
Block a user