瀏覽代碼

tcp_client example: support for Linux target.

Abhik Roy 3 年之前
父節點
當前提交
933a2a69fe

+ 2 - 0
examples/protocols/.build-test-rules.yml

@@ -229,6 +229,8 @@ examples/protocols/sockets/tcp_client:
     - if: IDF_TARGET != "esp32"
       temporary: true
       reason: lack of runners
+  enable:
+    - if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux"
 
 examples/protocols/sockets/tcp_client_multi_net:
   disable:

+ 9 - 5
examples/protocols/sockets/tcp_client/CMakeLists.txt

@@ -1,10 +1,14 @@
-# The following five lines of boilerplate have to be in your project's
+# The following lines of boilerplate have to be in your project's
 # CMakeLists in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.16)
 
-# (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)
+if(${IDF_TARGET} STREQUAL "linux")
+    list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
+    set(COMPONENTS main)
+    include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+else()
+    set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
+    include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+endif()
 
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(tcp_client)

+ 39 - 25
examples/protocols/sockets/tcp_client/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | Linux |
+| ----------------- | ----- | -------- | -------- | -------- | -------- | ----- |
 
 
 # TCP Client example
@@ -8,13 +8,38 @@
 
 The application creates a TCP socket and tries to connect to the server with predefined IP address and port number. When a connection is successfully established, the application sends message and waits for the answer. After the server's reply, application prints received reply as ASCII text, waits for 2 seconds and sends another message.
 
-## How to use example
+## Configure the project
+This example can be configured to run on ESP32 and Linux target to communicate over IPv4 and IPv6.
+
+```
+idf.py menuconfig
+```
+
+Set following parameters under ```Example Configuration``` Options:
+
+* Set `IP version` of example to be IPV4 or IPV6.
+
+* Set `IPV4 Address` in case your chose IP version IPV4 above.
+
+* Set `IPV6 Address` in case your chose IP version IPV6 above.
+    * For IPv6 there's an additional option for ```Interface selection```.
+    * Enter the name of the interface to explicitely establish communication over a specific interface.
+    * On selecting ```Auto``` the example will find the first interface with an IPv6 address and use it.
+
+* Set `Port` number that represents remote port the example will connect to.
+
+Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
+
+
+Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command.
+
+
+## How to use example:
 
 In order to create TCP server that communicates with TCP Client example, choose one of the following options.
 
 There are many host-side tools which can be used to interact with the UDP/TCP server/client.
 One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
-Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command.
 
 In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
 
@@ -24,35 +49,24 @@ nc -l 192.168.0.167 3333
 ```
 
 ### Python scripts
-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:
+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. 
 
+Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported.
+Please run the following commands to configure the terminal to execute the script.
 ```
-python example_test.py IPv4
+export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages"
+python -m pip install -r $IDF_PATH/tools/ci/python_packages/ttfw_idf/requirements.txt
 ```
-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
-
-This example can be run on any commonly available ESP32 development board.
-
-## Configure the project
 
+Example:
 ```
-idf.py menuconfig
+python example_test.py IPv4
 ```
+## Hardware Required
 
-Set following parameters under Example Configuration Options:
-
-* Set `IP version` of example to be IPV4 or IPV6.
-
-* Set `IPV4 Address` in case your chose IP version IPV4 above.
-
-* Set `IPV6 Address` in case your chose IP version IPV6 above.
-
-* Set `Port` number that represents remote port the example will connect to.
+This example can be run on any commonly available ESP32 development board.
+This example can also run on any Linux environment.
 
-Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
 
 ## Build and Flash
 

+ 6 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/CMakeLists.txt

@@ -0,0 +1,6 @@
+if(${IDF_TARGET} STREQUAL "linux")
+    idf_component_register(SRCS
+                        esp_stubs/esp_stubs.c
+                        INCLUDE_DIRS . include/stubs
+                        REQUIRES main)
+endif()

+ 45 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/esp_stubs/esp_stubs.c

@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include <stdlib.h>
+#include "esp_err.h"
+#include "esp_log.h"
+
+extern void app_main(void);
+
+void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
+{
+    ESP_LOGE("ESP_ERROR_CHECK", "Failed with esp_err_t: 0x%x", rc);
+    ESP_LOGE("ESP_ERROR_CHECK", "Expression: %s", expression);
+    ESP_LOGE("ESP_ERROR_CHECK", "Functions: %s %s(%d)", function, file, line);
+    abort();
+}
+
+esp_err_t esp_event_loop_create_default(void)
+{
+    return ESP_OK;
+}
+
+esp_err_t esp_netif_init(void)
+{
+    return ESP_OK;
+}
+
+esp_err_t example_connect(void)
+{
+    return ESP_OK;
+}
+
+esp_err_t nvs_flash_init(void)
+{
+    return ESP_OK;
+}
+
+int main()
+{
+    app_main();
+
+    return 0;
+}

+ 8 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_event.h

@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "esp_err.h"
+
+esp_err_t esp_event_loop_create_default(void);

+ 11 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_netif.h

@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include "esp_err.h"
+
+esp_err_t esp_netif_init(void);

+ 8 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/nvs_flash.h

@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "esp_err.h"
+
+esp_err_t nvs_flash_init(void);

+ 8 - 0
examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/protocol_examples_common.h

@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "esp_err.h"
+
+esp_err_t example_connect(void);

+ 13 - 2
examples/protocols/sockets/tcp_client/main/CMakeLists.txt

@@ -1,2 +1,13 @@
-idf_component_register(SRCS "tcp_client.c"
-                    INCLUDE_DIRS ".")
+if(${IDF_TARGET} STREQUAL "linux")
+    set(requires esp_stubs)
+endif()
+
+if("${CONFIG_EXAMPLE_IPV4}" STREQUAL y)
+    set(tcp_client_ip tcp_client_v4.c)
+else()
+    set(tcp_client_ip tcp_client_v6.c)
+endif()
+
+idf_component_register(SRCS "tcp_client_main.c" "${tcp_client_ip}"
+                                INCLUDE_DIRS "."
+                                REQUIRES ${requires})

+ 20 - 0
examples/protocols/sockets/tcp_client/main/Kconfig.projbuild

@@ -35,6 +35,26 @@ menu "Example Configuration"
         help
             The remote port to which the client example will connect to.
 
+    choice EXAMPLE_INTERFACE
+        prompt "Interface selection"
+        depends on EXAMPLE_IPV6
+        help
+            Example can use either "Auto" or "User specified".
+
+        config EXAMPLE_IFACE_AUTO
+            bool "Auto"
+
+        config EXAMPLE_USER_SPECIFIED_IFACE
+            bool "User specified interface"
+    endchoice
+
+    config EXAMPLE_USER_SPECIFIED_IFACE_NAME
+        string "User specified interface name"
+        default "st1"
+        depends on EXAMPLE_USER_SPECIFIED_IFACE
+        help
+            This interface will be used for communication.
+
     choice EXAMPLE_SOCKET_IP_INPUT
         prompt "Socket example source"
         default EXAMPLE_SOCKET_IP_INPUT_STRING

+ 27 - 0
examples/protocols/sockets/tcp_client/main/tcp_client_main.c

@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "nvs_flash.h"
+#include "esp_netif.h"
+#include "protocol_examples_common.h"
+#include "esp_event.h"
+
+
+extern void tcp_client(void);
+
+void app_main(void)
+{
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
+     * Read "Establishing Wi-Fi or Ethernet Connection" section in
+     * examples/protocols/README.md for more information about this function.
+     */
+    ESP_ERROR_CHECK(example_connect());
+
+    tcp_client();
+}

+ 20 - 53
examples/protocols/sockets/tcp_client/main/tcp_client.c → examples/protocols/sockets/tcp_client/main/tcp_client_v4.c

@@ -1,33 +1,24 @@
-/* BSD Socket API Example
-
-   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.
-*/
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "sdkconfig.h"
 #include <string.h>
-#include <sys/param.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/event_groups.h"
-#include "esp_system.h"
-#include "esp_wifi.h"
-#include "esp_event.h"
-#include "esp_log.h"
-#include "nvs_flash.h"
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netdb.h>            // struct addrinfo
+#include <arpa/inet.h>
 #include "esp_netif.h"
-#include "protocol_examples_common.h"
+#include "esp_log.h"
+#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
 #include "addr_from_stdin.h"
-#include "lwip/err.h"
-#include "lwip/sockets.h"
-
+#endif
 
 #if defined(CONFIG_EXAMPLE_IPV4)
 #define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
-#elif defined(CONFIG_EXAMPLE_IPV6)
-#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
-#else
+#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
 #define HOST_IP_ADDR ""
 #endif
 
@@ -36,7 +27,8 @@
 static const char *TAG = "example";
 static const char *payload = "Message from ESP32 ";
 
-static void tcp_client_task(void *pvParameters)
+
+void tcp_client(void)
 {
     char rx_buffer[128];
     char host_ip[] = HOST_IP_ADDR;
@@ -46,23 +38,16 @@ static void tcp_client_task(void *pvParameters)
     while (1) {
 #if defined(CONFIG_EXAMPLE_IPV4)
         struct sockaddr_in dest_addr;
-        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
+        inet_pton(AF_INET, host_ip, &dest_addr.sin_addr);
         dest_addr.sin_family = AF_INET;
         dest_addr.sin_port = htons(PORT);
         addr_family = AF_INET;
         ip_protocol = IPPROTO_IP;
-#elif defined(CONFIG_EXAMPLE_IPV6)
-        struct sockaddr_in6 dest_addr = { 0 };
-        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;
 #elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
         struct sockaddr_storage 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);
@@ -70,7 +55,7 @@ static void tcp_client_task(void *pvParameters)
         }
         ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
 
-        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
+        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
         if (err != 0) {
             ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
             break;
@@ -96,8 +81,6 @@ static void tcp_client_task(void *pvParameters)
                 ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                 ESP_LOGI(TAG, "%s", rx_buffer);
             }
-
-            vTaskDelay(2000 / portTICK_PERIOD_MS);
         }
 
         if (sock != -1) {
@@ -106,20 +89,4 @@ static void tcp_client_task(void *pvParameters)
             close(sock);
         }
     }
-    vTaskDelete(NULL);
-}
-
-void app_main(void)
-{
-    ESP_ERROR_CHECK(nvs_flash_init());
-    ESP_ERROR_CHECK(esp_netif_init());
-    ESP_ERROR_CHECK(esp_event_loop_create_default());
-
-    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
-     * Read "Establishing Wi-Fi or Ethernet Connection" section in
-     * examples/protocols/README.md for more information about this function.
-     */
-    ESP_ERROR_CHECK(example_connect());
-
-    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
 }

+ 255 - 0
examples/protocols/sockets/tcp_client/main/tcp_client_v6.c

@@ -0,0 +1,255 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "sdkconfig.h"
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netdb.h>            // struct addrinfo
+#include <arpa/inet.h>
+#include "esp_netif.h"
+#include "esp_log.h"
+#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
+#include "addr_from_stdin.h"
+#endif
+
+#if defined(CONFIG_EXAMPLE_IPV6_ADDR)
+#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
+#else
+#define HOST_IP_ADDR ""
+#endif
+
+#define PORT CONFIG_EXAMPLE_PORT
+
+static const char *TAG = "example";
+static const char *payload = "Message from ESP32 ";
+
+#if defined(CONFIG_IDF_TARGET_LINUX)
+// Checks for Global address, Unique Unicast(RFC4193) and link-local address.
+#define ip6_addr_isglobal(ip6addr) ((((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xe0000000UL)) & htonl(0x20000000UL)) || \
+                                    (((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfc000000UL)) || \
+                                    (((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfe800000UL)))
+
+
+/**
+ * @brief In case of Auto mode returns the interface name with a valid IPv6 address or
+ *        In case the user has specified interface, validates and returns the interface name.
+ *
+ * @param[out] interface Name of the interface in as a string.
+ *
+ * @return 0 incase of success.
+ */
+static int get_src_iface(char *interface)
+{
+    struct ifaddrs *ifap, *ifa;
+    char src_addr_str[INET6_ADDRSTRLEN];
+
+    if (getifaddrs(&ifap) == -1) {
+        ESP_LOGE(TAG, "getifaddrs failed");
+        return -1;
+    }
+
+    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+        if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
+#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
+            if (0 == strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, ifa->ifa_name)) {
+                strcpy(interface, ifa->ifa_name);
+                freeifaddrs(ifap);
+                ESP_LOGI(TAG, "Interface: %s", interface);
+                return 0;
+            }
+#else
+            strcpy(interface, ifa->ifa_name);
+            getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), src_addr_str,
+                        sizeof(src_addr_str), NULL, 0, NI_NUMERICHOST);
+
+            struct sockaddr_in6 *src_addr = (struct sockaddr_in6 *) ifa->ifa_addr;
+            inet_ntop(AF_INET6, &(src_addr->sin6_addr), src_addr_str, INET6_ADDRSTRLEN);
+
+            if (ip6_addr_isglobal(src_addr)) {
+                //Return as we have the source address
+                freeifaddrs(ifap);
+                ESP_LOGI(TAG, "Interface: %s", interface);
+                return 0;
+            }
+#endif    // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
+        }
+    }
+
+    freeifaddrs(ifap);
+    return -1;
+}
+#else
+
+static esp_netif_t *get_esp_netif_from_iface(char *interface_i)
+{
+    esp_netif_t *netif = NULL;
+    esp_err_t ret = ESP_FAIL;
+    char iface[10];
+
+    // Get interface details and own global ipv6 address
+    for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
+        netif = esp_netif_next(netif);
+        ret = esp_netif_get_netif_impl_name(netif, iface);
+        if ((ESP_FAIL == ret) || (NULL == netif)) {
+            ESP_LOGE(TAG, "No interface available");
+            return NULL;
+        }
+
+        if (0 == strcmp(interface_i, iface)) {
+            return netif;
+        }
+    }
+
+    return NULL;
+}
+
+
+/**
+ * @brief In case of Auto mode returns the interface name with a valid IPv6 address or
+ *        In case the user has specified interface, validates and returns the interface name.
+ *
+ * @param[out] interface Name of the interface in as a string.
+ *
+ * @return 0 incase of success.
+ */
+static int get_src_iface(char *interface)
+{
+    esp_netif_t *netif = NULL;
+    esp_err_t ret = ESP_FAIL;
+    int ip6_addrs_count = 0;
+    esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES];
+
+    // Get interface details and own global ipv6 address
+    for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
+        netif = esp_netif_next(netif);
+        ret = esp_netif_get_netif_impl_name(netif, interface);
+
+        if ((ESP_FAIL == ret) || (NULL == netif)) {
+            ESP_LOGE(TAG, "No interface available");
+            return -1;
+        }
+
+#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
+        if (!strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, interface)) {
+            ESP_LOGI(TAG, "Interface: %s", interface);
+            return 0;
+        }
+#else
+        ip6_addrs_count = esp_netif_get_all_ip6(netif, ip6);
+        for (int j = 0; j < ip6_addrs_count; ++j) {
+            esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
+
+            if ((ESP_IP6_ADDR_IS_GLOBAL == ipv6_type) ||
+                    (ESP_IP6_ADDR_IS_UNIQUE_LOCAL == ipv6_type) ||
+                    (ESP_IP6_ADDR_IS_LINK_LOCAL == ipv6_type)) {
+                // Break as we have the source address
+                ESP_LOGI(TAG, "Interface: %s", interface);
+                return 0;
+            }
+        }
+#endif    // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
+    }
+
+    return -1;
+}
+#endif    // #if defined(CONFIG_IDF_TARGET_LINUX)
+
+
+void tcp_client(void)
+{
+    char rx_buffer[128];
+    char host_ip[] = HOST_IP_ADDR;
+    int addr_family = 0;
+    int ip_protocol = 0;
+    char interface[10];
+#if defined(CONFIG_IDF_TARGET_LINUX)
+    struct ifreq ifr;
+#else
+    esp_netif_t *netif = NULL;
+#endif
+
+    while (1) {
+#if defined(CONFIG_EXAMPLE_IPV6)
+        struct sockaddr_in6 dest_addr = { 0 };
+        inet_pton(AF_INET6, host_ip, &dest_addr.sin6_addr);
+        dest_addr.sin6_family = AF_INET6;
+        dest_addr.sin6_port = htons(PORT);
+        addr_family = AF_INET6;
+        ip_protocol = IPPROTO_TCP;
+#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
+        struct sockaddr_storage 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, PORT);
+
+        if (0 != get_src_iface(interface)) {
+            ESP_LOGE(TAG, "Interface: Unavailable\n");
+            break;
+        }
+
+#if defined(CONFIG_IDF_TARGET_LINUX)
+        memset (&ifr, 0, sizeof(ifr));
+        snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
+        if (ioctl (sock, SIOCGIFINDEX, &ifr) < 0) {
+            ESP_LOGE(TAG, "ioctl() failed to find interface ");
+            break;
+        }
+#if defined(CONFIG_EXAMPLE_IPV6)
+        dest_addr.sin6_scope_id = ifr.ifr_ifindex;
+        ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id);
+#endif
+#else
+        if (NULL == (netif = get_esp_netif_from_iface(interface))) {
+            ESP_LOGE(TAG, "Failed to find interface ");
+            break;
+        }
+#if defined(CONFIG_EXAMPLE_IPV6)
+        dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(netif);
+        ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id);
+#endif
+#endif
+
+        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
+        if (err != 0) {
+            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
+            break;
+        }
+        ESP_LOGI(TAG, "Successfully connected");
+
+        while (1) {
+            int err = send(sock, payload, strlen(payload), 0);
+            if (err < 0) {
+                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
+                break;
+            }
+
+            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
+            // Error occurred during receiving
+            if (len < 0) {
+                ESP_LOGE(TAG, "recv failed: errno %d", errno);
+                break;
+            }
+            // 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, host_ip);
+                ESP_LOGI(TAG, "%s", rx_buffer);
+            }
+        }
+
+        if (sock != -1) {
+            ESP_LOGE(TAG, "Shutting down socket and restarting...");
+            shutdown(sock, 0);
+            close(sock);
+        }
+    }
+}

+ 4 - 0
examples/protocols/sockets/tcp_client/sdkconfig.defaults

@@ -0,0 +1,4 @@
+# This file was generated using idf.py save-defconfig. It can be edited manually.
+# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
+#
+# CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER is not set