Trying to reduce TCP Socket Latency

Sacingram
Posts: 2
Joined: Tue Jan 05, 2021 3:36 am

Trying to reduce TCP Socket Latency

Postby Sacingram » Tue Jan 05, 2021 5:57 am

Hi All,

I’m currently working on prototyping WIFI connection between my ESP-WROVER-KIT and my PC. I have some questions about my methods and results.

The aim of this prototype was to get a network latency between the ESP32 and the PC as low as possible (around 6-10ms would be great) with a consistent packet size of 512 bytes. Currently the PC is connected to a WIFI router via ethernet cable.As this is my first foray into networking, I did a little research and came to the conclusion that a TCP socket connection would be a well documented method with the possibility of getting the data latency required. I used the esp-idf socket examples for the ESP32 and online example code for a Windows Forms application for my PC as my code bases with some modifications to measure return trip time and send the required packet size. The measuring of return trip time was done by adding a stopwatch(Windows Forms) or upwards timer(ESP32) and measuring the time between sending and receiving packets in a blocking call.

The first test had the ESP32 as the server acting as an echo, and the Windows forms was the client, sending a 512 byte packet and timing how long it took until it returned. The recorded return trip time of this transaction was approximately 40-50ms. This value was confirmed using Wireshark.

ESP32 Server Echo

Code: Select all

/* 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.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


#define PORT CONFIG_EXAMPLE_PORT



static const char *TAG = "example";

static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[513];

    do {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        } else if (len == 0) {
            ESP_LOGW(TAG, "Connection closed");
        } else {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // send() can return less bytes than supplied length.
            // Walk-around for robust implementation. 
            int to_write = len;
            while (to_write > 0) {
                int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
                if (written < 0) {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                }
                to_write -= written;
	            //vTaskDelay(10);
            }
        }
    } while (len > 0);
}

static void tcp_server_task(void *pvParameters)
{
    char addr_str[513];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    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) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        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);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Convert ip address to string
        if (source_addr.sin6_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
        } else if (source_addr.sin6_family == PF_INET6) {
            inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_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());

#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
}

Windows Forms Client Send/Receive/Time

Code: Select all

using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;

public class GetSocket
{
    private static Socket ConnectSocket()
    {
        Socket socket = null;
        IPHostEntry hostEntry = null;

        // Get host related information.


        // Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
        // an exception that occurs when the host IP Address is not compatible with the address family
        // (typical in the IPv6 case).

        IPEndPoint ipe = new IPEndPoint(771860672, 3333);
        ///IPEndPoint ipe = new IPEndPoint(2046929088, 3333);
        /*Socket tempSocket =
            new Socket(ipe.AddressFamily, SocketType.Dgram, ProtocolType.Udp);*/
        Socket tempSocket =
               new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        tempSocket.Connect(ipe);

            if(tempSocket.Connected)
            {
            socket = tempSocket;

            }

        return socket;
    }

    // This method requests the home page content for the specified server.
    private static string SocketSendReceive(string server, int port)
    {
        string request = "abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdfeghijhk";
        Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
        Byte[] bytesReceived = new Byte[512];
        string page = "";
        

        // Create a socket connection with the specified server and port.
        using (Socket socket = ConnectSocket()) {

            if (socket == null)
                return ("Connection failed");

            socket.NoDelay = true; /*Need to remove for UDP connection*/
            int dataLengthReceived = 0;
            UInt32[] bytearray = new UInt32[512];
            int index = 0;
            double[] timearraymili = new double[512];
            
            int timindex = 0;
            byte sendCount = 0;
            socket.ReceiveTimeout = 0;
            Stopwatch stopWatch = new Stopwatch();
            TimeSpan ts = stopWatch.Elapsed;
            do
            {
                
                ts = TimeSpan.Zero;

                for(var count = 0; count < bytesSent.Length; count++)
                {
                    bytesSent[count] = (byte)sendCount;
                }

                stopWatch.Reset();
                stopWatch.Start();
                socket.Send(bytesSent, bytesSent.Length, 0);
                dataLengthReceived = socket.Receive(bytesReceived, bytesReceived.Length, 0);
                stopWatch.Stop();
                ts = stopWatch.Elapsed;

                if(dataLengthReceived == 512)
                {
                    timearraymili[timindex] = ts.TotalMilliseconds;
                    timindex++;

                    Debug.WriteLine("Send Count {0} -  Latency = {1} ms", sendCount, ts.TotalMilliseconds.ToString("0.000"));

                    for(var count = 0; count < bytesReceived.Length; count++)
                    {
                        if(bytesReceived[count] != sendCount)
                        {
                            Debug.WriteLine("Invalid Data Received {0} != {1}", bytesReceived[count], sendCount);
                        }
                    }
                }
                else
                {
                    Debug.WriteLine("bytes received != 512: {0} - elapsed{2}",  dataLengthReceived.ToString(), stopWatch.ElapsedMilliseconds);
                }
                sendCount++;
                //Thread.Sleep(100);

            }
            while (dataLengthReceived > 0);
        }

        return page;
    }

    public static void Main(string[] args)
    {
        string host;
        int port = 3333;

        if (args.Length == 0)
            // If no server name is passed as argument to this program,
            // use the current host name as the default.
            host = Dns.GetHostName();
        else
            host = args[0];

        string result = SocketSendReceive(host, port);
        Console.WriteLine(result);
    }
}
The second test had the ESP32 as the client acting as an echo, and the Windows forms was the server, sending a 512 byte packet and timing how long it took until it returned. The recorded return trip time of this transaction was approximately 40-50ms. This value was confirmed using Wireshark.

ESP32 Client Echo

Code: Select all

/* 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.
*/
#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 "esp_netif.h"
#include "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"


#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
#define HOST_IP_ADDR ""
#endif

#define PORT CONFIG_EXAMPLE_PORT

static const char *TAG = "example";


static void tcp_client_task(void *pvParameters)
{
	int len;
	char rx_buffer[513];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;

    while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
        struct sockaddr_in dest_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;
#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_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, PORT);

        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;
        }
        ESP_LOGI(TAG, "Successfully connected");

	    do {
		    len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
		    if (len < 0) {
			    ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
		    }
		    else if (len == 0) {
			    ESP_LOGW(TAG, "Connection closed");
		    }
		    else {
			    rx_buffer[len] = 0;  
			    ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);


			    int to_write = len;
			    while (to_write > 0) {
				    int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
				    if (written < 0) {
					    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
				    }
				    to_write -= written;

			    }
		    }
	    } while (len > 0);

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            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);
}

Windows Forms Server Send/Receive/Time

Code: Select all

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;
using System.Threading;

class MyTcpListener
{
    public static void Main()
    {
        TcpListener server = null;
        try
        {
            // Set the TcpListener on port 13000.
            Int32 port = 3333;
            IPAddress localAddr = IPAddress.Parse("192.168.1.199");

            // TcpListener server = new TcpListener(port);
            server = new TcpListener(localAddr, port);

            // Start listening for client requests.
            server.Start();

            // Buffer for reading data
            string request = "abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdfeghijhk";
            Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
            Byte[] bytesReceived = new Byte[512];
            
            int dataLengthReceived = 0;
            String dataReceived;
            UInt32[] bytearray = new UInt32[512];
            double[] timearraymili = new double[512];

            int timindex = 0;
            byte sendCount = 0;
            Stopwatch stopWatch = new Stopwatch();
            TimeSpan ts = stopWatch.Elapsed;

            // Enter the listening loop.
            while (true)
            {
                Debug.Write("Waiting for a connection... ");

                // Perform a blocking call to accept requests.
                // You could also use server.AcceptSocket() here.
                TcpClient client = server.AcceptTcpClient();
                Debug.WriteLine("Connected!");

                
                // Get a stream object for reading and writing
                NetworkStream stream = client.GetStream();
                                
                do
                {
                    ts = TimeSpan.Zero;

                    for (var count = 0; count < bytesSent.Length; count++)
                    {
                        bytesSent[count] = (byte)sendCount;
                    }

                    stopWatch.Reset();
                    stopWatch.Start();
                    stream.Write(bytesSent, 0, bytesSent.Length);
                    stream.Read(bytesReceived, 0, bytesReceived.Length);
                    stopWatch.Stop();
                    ts = stopWatch.Elapsed;
                    dataReceived = System.Text.Encoding.ASCII.GetString(bytesReceived, 0, bytesReceived.Length);
                    dataLengthReceived = dataReceived.Length;

                    if (dataLengthReceived == 512)
                    {
                        timearraymili[timindex] = ts.TotalMilliseconds;
                        timindex++;

                        Debug.WriteLine("Send Count {0} -  Latency = {1} ms", sendCount, ts.TotalMilliseconds.ToString("0.000"));

                        for (var count = 0; count < bytesReceived.Length; count++)
                        {
                            if (bytesReceived[count] != sendCount)
                            {
                                Debug.WriteLine("Invalid Data Received {0} != {1}", bytesReceived[count], sendCount);
                            }
                        }
                    }
                    else
                    {
                        Debug.WriteLine("bytes received != 512: {0} - elapsed{2}", dataLengthReceived.ToString(), stopWatch.ElapsedMilliseconds);
                    }
                    sendCount++;


                }
                while (dataLengthReceived > 0);
                // Shutdown and end connection
                client.Close();
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine("SocketException: {0}", e);
        }
        finally
        {
            // Stop listening for new clients.
            server.Stop();
        }

        Console.WriteLine("\nHit enter to continue...");
        Console.Read();
    }
}
The third test had the ESP32 as the client sending a 512 byte packet and timing how long it took until it returned, and the Windows forms was the server acting as an echo. The recorded return trip time of this transaction was approximately 6-10ms (the return trip time value was sent in the next packet). This value was confirmed with Wireshark while measuring the return trip from the point of the ESP32 (ESP32-PC-ESP32). However if measured in Wireshark from the perspective of the PC (PC-ESP32-PC), the return trip time is approximately 40-50ms.

ESP32 Client Server Send/Receive/Time

Code: Select all

/* 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.
*/
#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 "esp_netif.h"
#include "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "timer_types.h"
#include "timer.h"
#include <esp_app_trace.h>


#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
#define HOST_IP_ADDR ""
#endif

#define PORT CONFIG_EXAMPLE_PORT

static const char *TAG = "example";
char ESP_Tx_512Bytes[512] = "Message from ESP32333 ";

static void tcp_client_task(void *pvParameters)
{
	char rx_buffer[513];
	char host_ip[] = HOST_IP_ADDR;
	int addr_family = 0;
	int ip_protocol = 0;
	double stopwatchtime;

	
	timer_config_t stopwatch;
	stopwatch.divider = 80;
	stopwatch.counter_dir = TIMER_COUNT_UP;
	stopwatch.alarm_en = TIMER_ALARM_DIS;
	timer_init(TIMER_GROUP_0, 0, &stopwatch);

	while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
		struct sockaddr_in dest_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;
#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_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, PORT);

		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;
		}
		ESP_LOGI(TAG, "Successfully connected");

		while (1) {
			timer_set_counter_value(TIMER_GROUP_0, 0, 0);
			timer_start(TIMER_GROUP_0, 0);
			int err = send(sock, ESP_Tx_512Bytes, strlen(ESP_Tx_512Bytes), 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
				timer_pause(TIMER_GROUP_0, 0);
				timer_get_counter_time_sec(TIMER_GROUP_0, 0, &stopwatchtime);
				ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
				ESP_LOGI(TAG, "%s", rx_buffer);
				ESP_LOGI(TAG, "Latency: %lf seconds", stopwatchtime);
	            
				sprintf(ESP_Tx_512Bytes, "Latency: %lf seconds PacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePacketSizePZPZ", stopwatchtime);
					            
			}
			
		}

		if (sock != -1) {
			ESP_LOGE(TAG, "Shutting down socket and restarting...");
			shutdown(sock, 0);
			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);
}

Windows Forms Echo

Code: Select all

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

class MyTcpListener
{
    public static void Main()
    {
        TcpListener server = null;
        try
        {
            // Set the TcpListener on port 13000.
            Int32 port = 3333;
            IPAddress localAddr = IPAddress.Parse("192.168.1.199");

            // TcpListener server = new TcpListener(port);
            server = new TcpListener(localAddr, port);

            // Start listening for client requests.
            server.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[513];
            String data = null;

            // Enter the listening loop.
            while (true)
            {
                Console.Write("Waiting for a connection... ");

                // Perform a blocking call to accept requests.
                // You could also use server.AcceptSocket() here.
                TcpClient client = server.AcceptTcpClient();
                Console.WriteLine("Connected!");

                data = null;

                // Get a stream object for reading and writing
                NetworkStream stream = client.GetStream();

                int i;

                // Loop to receive all the data sent by the client.
                while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                {
                    // Translate data bytes to a ASCII string.
                    data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                    Console.WriteLine("Received: {0}", data);

                    // Process the data sent by the client.
                    data = data.ToUpper();

                    byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                    // Send back a response.
                    stream.Write(msg, 0, msg.Length);
                    Console.WriteLine("Sent: {0}", data);
                }

                // Shutdown and end connection
                client.Close();
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine("SocketException: {0}", e);
        }
        finally
        {
            // Stop listening for new clients.
            server.Stop();
        }

        Console.WriteLine("\nHit enter to continue...");
        Console.Read();
    }
}
Questions:
In the case of Test 3, does the return trip time measured with wireshark indicate that the biggest delay is from the processing of the data through the ESP32? Or am I misinterpreting the data?
Is my method of testing return trip time in the Windows form application (stopwatch) and ESP32 (timer counting upwards) in a blocking call accurate?
As the transactions from PC to ESP32 and back result in an average 40-50ms return trip time, are there any settings or code optimisations I should be aware of that help reduce this time?
If these are the best time I can expect, would you recommend any other protocols to achieve better return trip times?

Thanks for your time

ESP_Sprite
Posts: 9052
Joined: Thu Nov 26, 2015 4:08 am

Re: Trying to reduce TCP Socket Latency

Postby ESP_Sprite » Tue Jan 05, 2021 8:50 am

Without looking in detail into your results, you may want to disable Nagle's algorithm on the TCP socket (TCP_NODELAY).

Sacingram
Posts: 2
Joined: Tue Jan 05, 2021 3:36 am

Re: Trying to reduce TCP Socket Latency

Postby Sacingram » Wed Jan 06, 2021 4:38 am

Hi ESP_Sprite,

Thanks for the suggestion, I did read there were some others who had issues with Nargle's algorithm before. I tried disabling it on both sides (Visual GDB Project properties on the ESP32 and including socket.NoDelay = true; for the windows forms) but I still was getting the same roundtrip time. Do you know of any other settings that are typically or easily missed?

Thanks,

User avatar
Jakobsen
Posts: 89
Joined: Mon Jan 16, 2017 8:12 am

Re: Trying to reduce TCP Socket Latency

Postby Jakobsen » Sun Feb 07, 2021 10:54 pm

Also make sure you have disabled package aggregation in you tcp/ip stack

in
Menuconfig > component config --> WIFI ---> WiFi AMPDU TX <--- Disable
Menuconfig > component config --> WIFI ---> WiFi AMPDU RX <--- Disable

/J
Analog Digital IC designer / DevOps @ Merus Audio, Copenhagen, Denmark.
We do novel and best in class Audio amplifiers for consumer products.
Programmed assembler for C-64 back in 1980's, learned some electronics - hacking since then

Who is online

Users browsing this forum: No registered users and 186 guests