如何解决尝试减少 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 举报,一经查实,本站将立刻删除。