尝试减少 ESP32 上的 TCP 套接字延迟

如何解决尝试减少 ESP32 上的 TCP 套接字延迟

我目前正在为我的 ESP-WROVER-KIT 和我的 PC 之间的 WIFI 连接进行原型设计。我对我的方法和结果有一些疑问。

此原型的目的是使 ESP32 和 PC 之间的网络延迟尽可能低(大约 6-10 毫秒会很好),并且数据包大小一致,为 512 字节。目前 PC 通过以太网电缆连接到 WIFI 路由器。由于这是我第一次涉足网络,我做了一些研究并得出结论,TCP 套接字连接将是一种有据可查的方法,可以获取数据需要延迟。我使用 ESP32 的 esp-idf 套接字示例和我的 PC 的 Windows 窗体应用程序的在线示例代码作为我的代码库,并进行了一些修改以测量回程时间并发送所需的数据包大小。回程时间的测量是通过添加秒表(Windows Forms)或向上计时器(ESP32)并测量阻塞调用中发送和接收数据包之间的时间来完成的。

第一个测试让 ESP32 作为服务器充当回声,Windows 窗体是客户端,发送一个 512 字节的数据包并计时它返回之前所用的时间。该交易记录的回程时间约为 40-50 毫秒。此值已使用 Wireshark 确认。

ESP32 服务器回声


   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,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,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,(void*)AF_INET6,NULL);
#endif
}

Windows 窗体客户端发送/接收/时间

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);
    }
}

第二个测试让 ESP32 作为客户端充当回声,Windows 窗体是服务器,发送一个 512 字节的数据包,并计时它返回所需的时间。该交易记录的回程时间约为 40-50 毫秒。此值已使用 Wireshark 确认。

ESP32 客户端回声


   This example code is in the Public Domain (or CC0 licensed,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,&ip_protocol,&addr_family,&dest_addr));
#endif
        int sock =  socket(addr_family,ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG,errno);
            break;
        }
        ESP_LOGI(TAG,"Socket created,connecting to %s:%d",host_ip,PORT);

        int err = connect(sock,sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG,"Socket unable to connect: errno %d","Successfully connected");

        do {
            len = recv(sock,0);
            if (len < 0) {
                ESP_LOGE(TAG,errno);
            }
            else if (len == 0) {
                ESP_LOGW(TAG,"Connection closed");
            }
            else {
                rx_buffer[len] = 0;  
                ESP_LOGI(TAG,rx_buffer);


                int to_write = len;
                while (to_write > 0) {
                    int written = send(sock,0);
                    if (written < 0) {
                        ESP_LOGE(TAG,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",NULL,NULL);
}

Windows 窗体服务器发送/接收/时间

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,bytesSent.Length);
                    stream.Read(bytesReceived,bytesReceived.Length);
                    stopWatch.Stop();
                    ts = stopWatch.Elapsed;
                    dataReceived = System.Text.Encoding.ASCII.GetString(bytesReceived,bytesReceived.Length);
                    dataLengthReceived = dataReceived.Length;

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

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

                        for (var count = 0; count < bytesReceived.Length; count++)
                        {
                            if (bytesReceived[count] != sendCount)
                            {
                                Debug.WriteLine("Invalid Data Received {0} != {1}",sendCount);
                            }
                        }
                    }
                    else
                    {
                        Debug.WriteLine("bytes received != 512: {0} - elapsed{2}",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();
    }
}

第三个测试让 ESP32 作为客户端发送一个 512 字节的数据包并计时它返回之前所用的时间,Windows 表单是服务器充当回声。本次交易记录的回程时间约为6-10ms(回程时间值在下一个数据包中发送)。在测量从 ESP32 (ESP32-PC-ESP32) 点的回程时,Wireshark 确认了该值。但是如果从PC(PC-ESP32-PC)的角度在Wireshark中测量,回程时间约为40-50ms。

ESP32 客户端服务器发送/接收/时间


   This example code is in the Public Domain (or CC0 licensed,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,&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,"Successfully connected");

        while (1) {
            timer_set_counter_value(TIMER_GROUP_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,errno);
                break;
            }

            int len = recv(sock,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,&stopwatchtime);
                ESP_LOGI(TAG,"Received %d bytes from %s:",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,NULL);
}

Windows 窗体回显

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,bytes.Length)) != 0)
                {
                    // Translate data bytes to a ASCII string.
                    data = System.Text.Encoding.ASCII.GetString(bytes,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,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();
    }
}

问题: 在Test 3的情况下,用wireshark测得的回程时间是否表明最大的延迟来自通过ESP32处理数据?还是我误解了数据? 我在 Windows 窗体应用程序(秒表)和 ESP32(计时器向上计数)中测试回程时间的方法是否准确? 由于从 PC 到 ESP32 的交易并返回平均 40-50 毫秒的回程时间,是否有任何我应该注意的设置或代码优化有助于减少这个时间? 如果这是我能期待的最佳时间,您会推荐其他任何协议来实现更好的回程时间吗? 请注意,我在每种情况下都禁用了 TCP 套接字 (TCP_NODELAY) 上的 Nagle 算法,但这并没有影响结果。

感谢您的时间

解决方法

连接到 ESP32 时可以预期的延迟和抖动在很大程度上取决于所选通道上免费 WiFi 以太网的可用性。在拥塞的无线信道(意味着许多其他设备在广播)上,您通常会看到 100 毫秒以上的延迟,因为您的设备必须等待空闲的无线电时隙。

话虽如此,512 B 有效载荷在合理空闲的通道上的预期往返延迟通常低于 10 毫秒,但并非总是如此。每隔 1 秒使用 ICMP PING 进行测试(ESP32 中禁用了 WiFi 省电):

$ ping -s 512 192.168.199.124
PING 192.168.199.124 (192.168.199.124) 512(540) bytes of data.
520 bytes from 192.168.199.124: icmp_seq=1 ttl=255 time=8.89 ms
520 bytes from 192.168.199.124: icmp_seq=2 ttl=255 time=2.02 ms
520 bytes from 192.168.199.124: icmp_seq=3 ttl=255 time=7.50 ms
520 bytes from 192.168.199.124: icmp_seq=4 ttl=255 time=2.17 ms
520 bytes from 192.168.199.124: icmp_seq=5 ttl=255 time=2.17 ms
520 bytes from 192.168.199.124: icmp_seq=6 ttl=255 time=4.42 ms
520 bytes from 192.168.199.124: icmp_seq=7 ttl=255 time=1.90 ms
520 bytes from 192.168.199.124: icmp_seq=8 ttl=255 time=1.72 ms
520 bytes from 192.168.199.124: icmp_seq=9 ttl=255 time=27.8 ms
520 bytes from 192.168.199.124: icmp_seq=10 ttl=255 time=1.67 ms
520 bytes from 192.168.199.124: icmp_seq=11 ttl=255 time=1.73 ms
520 bytes from 192.168.199.124: icmp_seq=12 ttl=255 time=1.91 ms
520 bytes from 192.168.199.124: icmp_seq=13 ttl=255 time=10.7 ms
520 bytes from 192.168.199.124: icmp_seq=14 ttl=255 time=1.84 ms
520 bytes from 192.168.199.124: icmp_seq=15 ttl=255 time=1.80 ms
520 bytes from 192.168.199.124: icmp_seq=16 ttl=255 time=1.77 ms
520 bytes from 192.168.199.124: icmp_seq=17 ttl=255 time=3.93 ms
^C
--- 192.168.199.124 ping statistics ---
17 packets transmitted,17 received,0% packet loss,time 16020ms
rtt min/avg/max/mdev = 1.673/4.936/27.766/6.327 ms

但是,在使用 WiFi 时根本无法保证延迟 - 当您信号覆盖范围内的任何人决定在同一频道(或足够相邻的频道)上下载大文件或用微波炉加热三明治时,您就会被搞砸。

其次,低延迟的东西通常是用 UDP 来完成的。 TCP 是一种可靠的协议,它会重试传输,直到所有丢失的数据包都重新发送并且乱序数据包已被排序。接收器套接字只是等待 TCP 完成所有这些操作,然后才能获取数据,无丢失地按顺序传送。这意味着一些丢失的数据包可能会导致所有后续数据的显着延迟,直到交通堵塞被排出。如果您可以容忍丢包和乱序传送,请改用 UDP。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-