From dddc755000c7a11035ccc186e7cd190f073696e0 Mon Sep 17 00:00:00 2001 From: Kamil Sroka Date: Wed, 19 Dec 2018 09:16:10 +0100 Subject: [PATCH] [sample] add TCP echo server and client (#17) --- README.md | 7 + src/apps/test/CMakeLists.txt | 1 + src/apps/test/tcp_utils.c | 385 ++++++++++++++++++++++++++++++++ src/apps/test/user.c | 106 ++++++++- src/apps/test/user.h | 7 + third_party/lwip/CMakeLists.txt | 4 + 6 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 src/apps/test/tcp_utils.c diff --git a/README.md b/README.md index ac6292ff2..5d494fc9a 100644 --- a/README.md +++ b/README.md @@ -112,3 +112,10 @@ A sample config is given below: `CLOUDIOT_CERT` is the first certificate from [https://pki.google.com/roots.pem](https://pki.google.com/roots.pem) You can use command `test mqtt` to test connect and publish a message to Google Cloud IoT core. + +### TCP echo server and client +Commands: +- `tcp_echo_server port` starts TCP echo server on given `port`. +- `tcp_connect ipaddr port` connects to given TCP server. +- `tcp_send size count` sends `count` packets with given `size` to connected TCP server. At the end it prints statistics. +- `tcp_disconnect` disconnects from TCP server. \ No newline at end of file diff --git a/src/apps/test/CMakeLists.txt b/src/apps/test/CMakeLists.txt index 69eaf6d5d..da9c9a4df 100644 --- a/src/apps/test/CMakeLists.txt +++ b/src/apps/test/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required (VERSION 3.7) add_library(test_app ${CMAKE_CURRENT_SOURCE_DIR}/http.c ${CMAKE_CURRENT_SOURCE_DIR}/mqtt.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tcp_utils.c ${CMAKE_CURRENT_SOURCE_DIR}/user.c) target_link_libraries(test_app diff --git a/src/apps/test/tcp_utils.c b/src/apps/test/tcp_utils.c new file mode 100644 index 000000000..13396ccfe --- /dev/null +++ b/src/apps/test/tcp_utils.c @@ -0,0 +1,385 @@ +#include +#include + +#include + +#include +#include + +#include "openthread/openthread-freertos.h" + +#define MAX_SEND_SIZE 1024 + +struct ServerParams +{ + otInstance * mInstance; + uint16_t mPort; +}; + +struct ConnectParams +{ + otInstance * mInstance; + char mPeerAddr[50]; + uint16_t mPort; +}; + +struct SendParams +{ + otInstance * mInstance; + uint32_t mCount; + uint32_t mSize; +}; + +static int sClientSocket = -1; +static struct ServerParams sServerParams = {}; +static struct ConnectParams sConnectParams = {}; +static struct SendParams sSendParams = {}; + +TaskHandle_t sServerTask = NULL; +TaskHandle_t sClientTask = NULL; + +static void echoServerTask(void *p) +{ + struct ServerParams * params = (struct ServerParams *)p; + struct sockaddr_in6 saddr; + char res[MAX_SEND_SIZE]; + + int fd = socket(AF_INET6, SOCK_STREAM, 0); + int afd = -1; + int rval = 0; + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin6_family = AF_INET6; + saddr.sin6_port = htons(params->mPort); + + + if (fd < 0) + { + printf("tcp_echo_server: Failed to create socket\r\n"); + goto exit; + } + + rval = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (rval != 0) + { + printf("tcp_echo_server: Cannot bind socket\r\n"); + goto exit; + } + + rval = listen(fd, 5); + if (rval != 0) + { + printf("tcp_echo_server: Cannot listen on socket\r\n"); + goto exit; + } + else + { + printf("tcp_echo_server: Listening on port %u\r\n", ntohs(saddr.sin6_port)); + } + + afd = accept(fd, NULL, NULL); + + if (afd >= 0) + { + printf("tcp_echo_server: Client connected\r\n"); + } + else + { + printf("tcp_echo_server: Client connection error\r\n"); + goto exit; + } + + do + { + rval = recv(afd, res, sizeof(res) - 1, 0); + if (rval <= 0) + { + if (rval == 0) + { + printf("tcp_echo_server: Client closed connection\r\n"); + } + else + { + printf("tcp_echo_server: Connection was reset by client\r\n"); + } + goto exit; + } + + res[rval] = '\0'; + printf("tcp_echo_server: Received %dB: %s\r\n", rval, res); + + rval = send(afd, res, rval, 0); + if (rval <= 0) + { + printf("tcp_echo_server: Failed to send data\r\n"); + goto exit; + } + } while(1); + +exit: + if (fd >= 0) + { + shutdown(fd, SHUT_RDWR); + close(fd); + } + + if (afd >= 0) + { + shutdown(afd, SHUT_RDWR); + close(afd); + } + + printf("tcp_echo_server: Finished\r\n"); + + sServerTask = NULL; + vTaskDelete(NULL); +} + +void connectTask(void *p) +{ + struct ConnectParams * connect_params = (struct ConnectParams *)p; + + struct sockaddr_in6 daddr; + + int rval = 0; + + sClientSocket = socket(AF_INET6, SOCK_STREAM, 0); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin6_family = AF_INET6; + daddr.sin6_port = htons(connect_params->mPort); + inet_pton(AF_INET6, connect_params->mPeerAddr, &daddr.sin6_addr); + + if (sClientSocket < 0) + { + printf("tcp_client: Failed to create socket\r\n"); + goto exit; + } + + rval = connect(sClientSocket, (struct sockaddr *)&daddr, sizeof(daddr)); + if (rval != 0) + { + printf("tcp_client: Cannot connect to server\r\n"); + goto exit; + } + else + { + printf("tcp_client: Connected\r\n"); + sClientTask = NULL; + vTaskDelete(NULL); + return; + } + +exit: + if (sClientSocket >= 0) + { + shutdown(sClientSocket, SHUT_RDWR); + close(sClientSocket); + sClientSocket = -1; + } + + sClientTask = NULL; + vTaskDelete(NULL); +} + +void disconnectTask(void *p) +{ + UNUSED_VARIABLE(p); + + if (sClientSocket >= 0) + { + shutdown(sClientSocket, SHUT_RDWR); + close(sClientSocket); + sClientSocket = -1; + } + + printf("tcp_client: Disconnected\r\n"); + + sClientTask = NULL; + vTaskDelete(NULL); +} + +void sendTask(void *p) +{ + struct SendParams * send_params = (struct SendParams *)p; + + int rval = 0; + static const char req[] = { [0 ... (MAX_SEND_SIZE-1)] = 'A'}; + char res[MAX_SEND_SIZE + 1]; + + TickType_t start; + TickType_t stop; + uint32_t msec; + uint32_t sent = 0; + uint32_t recvd = 0; + uint32_t throughput; + + TickType_t lat_send_time; + TickType_t lat_recv_time; + uint64_t lat_sum = 0; + uint32_t lat_min = UINT32_MAX; + uint32_t lat_max = 0; + uint32_t lat; + + start = xTaskGetTickCount(); + + for (uint32_t i = 0; i < send_params->mCount; i++) + { + printf("tcp_client: Sending data\r\n"); + + sent = 0; + recvd = 0; + + lat_send_time = xTaskGetTickCount(); + + while(sent != send_params->mSize) + { + rval = send(sClientSocket, req + sent, send_params->mSize - sent, 0); + + if (rval <= 0) + { + printf("tcp_client: Failed to send data\r\n"); + goto exit; + } + + sent += rval; + } + + while (recvd != send_params->mSize) + { + rval = recv(sClientSocket, res + recvd, send_params->mSize - recvd, 0); + + if (rval <= 0) + { + if (rval == 0) + { + printf("tcp_client: Server closed connection\r\n"); + } + else + { + printf("tcp_client: Connection was reset by server\r\n"); + } + + shutdown(sClientSocket, SHUT_RDWR); + close(sClientSocket); + sClientSocket = -1; + goto exit; + } + + recvd += rval; + } + + lat_recv_time = xTaskGetTickCount(); + + lat = (lat_recv_time - lat_send_time) * portTICK_PERIOD_MS / 2; + lat_sum += lat; + + if (lat > lat_max) + { + lat_max = lat; + } + + if (lat < lat_min) + { + lat_min = lat; + } + + res[rval] = '\0'; + printf("tcp_client: Received %dB: %s\r\n", rval, res); + } + + stop = xTaskGetTickCount(); + msec = (stop - start) * portTICK_PERIOD_MS; + + throughput = (100 * 8 * send_params->mSize * send_params->mCount) / msec; + + printf("tcp_client: Data transmitted : %lu B\r\n", send_params->mSize * send_params->mCount); + printf("tcp_client: Time : %lu ms\r\n", msec); + printf("tcp_client: Throughput : %lu.%02lu Kb/s\r\n", throughput / 100, throughput % 100); + printf("tcp_client: Latency : Avg: %lu ms Min: %lu ms, Max: %lu ms\r\n", + (uint32_t)lat_sum / send_params->mCount, + lat_min, + lat_max); + +exit: + printf("tcp_client: Send finished\r\n"); + + sClientTask = NULL; + vTaskDelete(NULL); +} + +bool startTcpEchoServer(otInstance *aInstance, uint16_t aPort) +{ + if (sServerTask == NULL) + { + sServerParams.mInstance = aInstance; + sServerParams.mPort = aPort; + UNUSED_VARIABLE(xTaskCreate(echoServerTask, + "echo", + MAX_SEND_SIZE + 1024, + &sServerParams, + 2, + &sServerTask)); + + return true; + } + + return false; +} + +bool startTcpConnect(otInstance *aInstance, char * aPeer, uint16_t aPort) +{ + if ((sClientTask == NULL) && (sClientSocket < 0)) + { + sConnectParams.mInstance = aInstance; + sConnectParams.mPort = aPort; + strncpy(sConnectParams.mPeerAddr, aPeer, sizeof(sConnectParams.mPeerAddr)); + UNUSED_VARIABLE(xTaskCreate(connectTask, + "conn", + 2048, + &sConnectParams, + 2, + &sClientTask)); + + return true; + } + + return false; +} + +bool startTcpDisconnect(void) +{ + if ((sClientTask == NULL) && (sClientSocket >= 0)) + { + UNUSED_VARIABLE(xTaskCreate(disconnectTask, + "disc", + 2048, + NULL, + 2, + &sClientTask)); + + return true; + } + + return false; +} + +bool startTcpSend(otInstance *aInstance, uint32_t count, uint32_t size) +{ + if ((sClientTask == NULL) && (sClientSocket >= 0)) + { + sSendParams.mInstance = aInstance; + sSendParams.mCount = count; + sSendParams.mSize = size; + UNUSED_VARIABLE(xTaskCreate(sendTask, + "send", + MAX_SEND_SIZE + 1024, + &sSendParams, + 2, + &sClientTask)); + + return true; + } + + return false; +} diff --git a/src/apps/test/user.c b/src/apps/test/user.c index 33cc12a0f..fbdf8b092 100644 --- a/src/apps/test/user.c +++ b/src/apps/test/user.c @@ -8,6 +8,13 @@ TaskHandle_t gTestTask = NULL; +static otError parseLong(char *argv, long *aValue) +{ + char *endptr; + *aValue = strtol(argv, &endptr, 0); + return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE; +} + static void ProcessTest(int argc, char *argv[]) { if (argc < 1) @@ -29,7 +36,104 @@ static void ProcessTest(int argc, char *argv[]) } } -static const struct otCliCommand sCommands[] = {{"test", ProcessTest}}; +static void ProcessEchoServer(int argc, char *argv[]) +{ + int32_t port; + + if (argc != 1) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (parseLong(argv[0], &port) != OT_ERROR_NONE) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (!startTcpEchoServer(otxGetInstance(), (uint16_t)port)) + { + otCliUartAppendResult(OT_ERROR_BUSY); + return; + } +} + +static void ProcessConnect(int argc, char *argv[]) +{ + int32_t port; + + if (argc != 2) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (parseLong(argv[1], &port) != OT_ERROR_NONE) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (!startTcpConnect(otxGetInstance(), argv[0], (uint16_t)port)) + { + otCliUartAppendResult(OT_ERROR_BUSY); + return; + } +} + +static void ProcessDisconnect(int argc, char *argv[]) +{ + UNUSED_VARIABLE(argc); + UNUSED_VARIABLE(argv); + + if (!startTcpDisconnect()) + { + otCliUartAppendResult(OT_ERROR_BUSY); + return; + } +} + +bool startTcpSend(otInstance *aInstance, uint32_t count, uint32_t size); + +static void ProcessSend(int argc, char *argv[]) +{ + int32_t count; + int32_t size; + + if (argc != 2) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (parseLong(argv[1], &count) != OT_ERROR_NONE) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (parseLong(argv[0], &size) != OT_ERROR_NONE) + { + otCliUartAppendResult(OT_ERROR_PARSE); + return; + } + + if (!startTcpSend(otxGetInstance(), (uint16_t)count, (uint16_t)size)) + { + otCliUartAppendResult(OT_ERROR_BUSY); + return; + } +} + +static const struct otCliCommand sCommands[] = { + {"test", ProcessTest}, + {"tcp_echo_server", ProcessEchoServer}, + {"tcp_connect", ProcessConnect}, + {"tcp_disconnect", ProcessDisconnect}, + {"tcp_send", ProcessSend} +}; + void otxUserInit(void) { diff --git a/src/apps/test/user.h b/src/apps/test/user.h index a71c6c1b6..88181d876 100644 --- a/src/apps/test/user.h +++ b/src/apps/test/user.h @@ -32,6 +32,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -41,6 +43,11 @@ extern TaskHandle_t gTestTask; void httpTask(void *p); void mqttTask(void *p); +bool startTcpEchoServer(otInstance *aInstance, uint16_t aPort); +bool startTcpConnect(otInstance *aInstance, char * aPeer, uint16_t aPort); +bool startTcpDisconnect(void); +bool startTcpSend(otInstance *aInstance, uint32_t count, uint32_t size); + #ifdef __cplusplus } #endif diff --git a/third_party/lwip/CMakeLists.txt b/third_party/lwip/CMakeLists.txt index 430bf0d65..efd77aa8e 100644 --- a/third_party/lwip/CMakeLists.txt +++ b/third_party/lwip/CMakeLists.txt @@ -94,6 +94,10 @@ target_include_directories(lwip ${LWIP_DIR}/src/apps/altcp_tls ) +target_compile_definitions(lwip + PUBLIC + DEFAULT_ACCEPTMBOX_SIZE=10) + target_link_libraries(lwip PUBLIC freertos