使用 UDP 进行写入和读取之间的长时间延迟
Long delay between a write and a read using UDP
我有一个 Raspberry Pi 4 运行ning 一个 C++ 程序,它通过 UDP 接收和发送数据。 RPi 被设置为 UDP 服务器。
UDP.hpp的代码是:
#pragma once
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string>
using namespace std;
/////GLOBAL CONSTANTS/////
const int c_PORT = 8080;
class UDP
{
private:
int fdSocketUDP_; //File descriptor for UDP socket
int ClientAddressLength_; //Length of client address
struct sockaddr_in ServerAddress_; //Struct handling internet address for server
struct sockaddr_in ClientAddress_; //Struct handling internet address for client
public:
UDP(); //Initialize and bind socket
~UDP(); //Close socket
string readUDP(const int readSize); //Read via UDP protocol
void writeUDP(string message); //Write via UDP protocol
};
UDP.cpp 的代码是:
#include "udp.hpp"
UDP::UDP()
{
if ((fdSocketUDP_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //Create UDP socket
{
perror("Error - socket creation - udp.cpp");
exit(EXIT_FAILURE);
}
memset(&ServerAddress_, 0, sizeof(ServerAddress_)); //Sets ServerAddress_ to 0
memset(&ClientAddress_, 0, sizeof(ClientAddress_)); //Sets ClientAddress_ to 0
ServerAddress_.sin_family = AF_INET; //Address family, must be AF_INET = IPv4
ServerAddress_.sin_port = htons(c_PORT); //PORT number, convert PORT number to network byte order using htons()
ServerAddress_.sin_addr.s_addr = INADDR_ANY; //IP-Address of host (server IP), INADDR_ANY gets this IP Address
if (bind(fdSocketUDP_, (const struct sockaddr *)&ServerAddress_, sizeof(ServerAddress_)) < 0) //Bind the socket to ServerAddress_
{
perror("Error - socket bind - udp.cpp");
exit(EXIT_FAILURE);
}
}
UDP::~UDP()
{
close(fdSocketUDP_); //Close socket
}
string UDP::readUDP(const int readSize)
{
char readMsg[readSize] = {0}; //Read buffer
ClientAddressLength_ = sizeof(ClientAddress_);
if ((recvfrom(fdSocketUDP_, readMsg, readSize, 0, (struct sockaddr *)&ClientAddress_, (socklen_t *)&ClientAddressLength_)) < 0) //Receive data via UDP protocol
{
perror("Error - recvfrom - udp.cpp");
exit(EXIT_FAILURE);
}
string str(readMsg); //Convert char array to string
str = str.substr(0, readSize); //Make sure the string is the length of readsize
return str;
}
void UDP::writeUDP(string message)
{
//Make char array
int writeSize = message.size();
char writeMsg[writeSize + 1] = {'[=11=]'};
//Convert string message to char array
for (int i = 0; i < writeSize; i++)
{
writeMsg[i] = message[i];
}
if ((sendto(fdSocketUDP_, writeMsg, writeSize, 0, (const struct sockaddr *)&ClientAddress_, (socklen_t)ClientAddressLength_)) < 0) //Send data via UDP protocol
{
perror("Error - sendto - udp.cpp");
exit(EXIT_FAILURE);
}
}
然后我有一台 windows 10 笔记本电脑,运行ning 一个 Labview 程序,该程序也通过 UDP 接收和发送数据。笔记本电脑设置为 UDP 客户端。以下是 Labview 中 UDP 设置的一些示例。
图片1(打开UDP连接):
图2(关闭UDP连接):
图 3(在 Labview 中读写 UDP):
以上,笔记本电脑上的Labview程序向树莓派发送3(“103”)+37(未显示)字节数据,然后从树莓派接收16字节数据。
笔记本电脑和树莓派通过本地网络上的 LAN 电缆连接。 RPi 使用 IP 地址 10.10.10.10 和端口 8080,笔记本电脑使用 IP 地址 10.10.10.1 和端口 1000。
下面是 Wireshark 测量,它测量 RPi 和笔记本电脑之间不同的发送和接收命令之间的时间。
图 4(wireshark 测量):
RPi 使用 "Len=3" 来确定 C++ 代码中 运行 的功能。 "Len=52" 和 "Len=37" 是从笔记本电脑 (Labview) 发送到 RPi(C++ 代码)的数据。 "Len=16" 是从树莓派发送到笔记本电脑的数据。
笔记本电脑首先向树莓派发送3+52字节的数据(客户端向服务器发送数据)。然后笔记本电脑向 RPi 发送 3+37 字节的数据(客户端向服务器发送数据)。 RPi 然后将 16 个字节的数据发送回笔记本电脑(服务器将数据发送到客户端)......等等。
一条命令(3+52 字节或 3+37+16 字节)大约需要 8 毫秒才能完成,每条命令之间的延迟(平均)约为 2 毫秒。如您所见,RPi 和笔记本电脑之间的数据大小 "relatively" 很小(3/37/52 字节)。
现在我的问题是: 有时命令之间会有约 20 毫秒的延迟(比平均约 2 毫秒长 10 倍),我不知道为什么。 ..(这在图 4 中用红点显示)。这种延迟通常发生在 RPi(UDP 服务器)向笔记本电脑(UDP 客户端 - 16 字节数据)发送数据之后,但它可能发生在不同的地方,如图 4 所示(在笔记本电脑向 RPi 发送 52 字节之后) ).我认为它与UDP有关,也许与设置有关,也许与ARP有关,但我不知道。我试过对 RPi 超频,调整 RPi 上 C++ 程序的优先级,调整 C++ 代码,但这似乎不是瓶颈。
这就像笔记本电脑和树莓派之间的 UDP 连接有时是 "lost" 或 "paused",然后需要一些时间才能使连接恢复正常。
我找到了解决问题的办法。为了解决长时间延迟,我不得不降低 UDP 读取缓冲区,因为我只通过 UDP 发送小包。
为此,我格式化了 RPi 上的 sysctl.conf 文件,该文件位于 /etc 文件夹中。
我添加了以下行:
net.core.rmem_default = 4096
net.core.rmem_max = 4096
我有一个 Raspberry Pi 4 运行ning 一个 C++ 程序,它通过 UDP 接收和发送数据。 RPi 被设置为 UDP 服务器。
UDP.hpp的代码是:
#pragma once
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string>
using namespace std;
/////GLOBAL CONSTANTS/////
const int c_PORT = 8080;
class UDP
{
private:
int fdSocketUDP_; //File descriptor for UDP socket
int ClientAddressLength_; //Length of client address
struct sockaddr_in ServerAddress_; //Struct handling internet address for server
struct sockaddr_in ClientAddress_; //Struct handling internet address for client
public:
UDP(); //Initialize and bind socket
~UDP(); //Close socket
string readUDP(const int readSize); //Read via UDP protocol
void writeUDP(string message); //Write via UDP protocol
};
UDP.cpp 的代码是:
#include "udp.hpp"
UDP::UDP()
{
if ((fdSocketUDP_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //Create UDP socket
{
perror("Error - socket creation - udp.cpp");
exit(EXIT_FAILURE);
}
memset(&ServerAddress_, 0, sizeof(ServerAddress_)); //Sets ServerAddress_ to 0
memset(&ClientAddress_, 0, sizeof(ClientAddress_)); //Sets ClientAddress_ to 0
ServerAddress_.sin_family = AF_INET; //Address family, must be AF_INET = IPv4
ServerAddress_.sin_port = htons(c_PORT); //PORT number, convert PORT number to network byte order using htons()
ServerAddress_.sin_addr.s_addr = INADDR_ANY; //IP-Address of host (server IP), INADDR_ANY gets this IP Address
if (bind(fdSocketUDP_, (const struct sockaddr *)&ServerAddress_, sizeof(ServerAddress_)) < 0) //Bind the socket to ServerAddress_
{
perror("Error - socket bind - udp.cpp");
exit(EXIT_FAILURE);
}
}
UDP::~UDP()
{
close(fdSocketUDP_); //Close socket
}
string UDP::readUDP(const int readSize)
{
char readMsg[readSize] = {0}; //Read buffer
ClientAddressLength_ = sizeof(ClientAddress_);
if ((recvfrom(fdSocketUDP_, readMsg, readSize, 0, (struct sockaddr *)&ClientAddress_, (socklen_t *)&ClientAddressLength_)) < 0) //Receive data via UDP protocol
{
perror("Error - recvfrom - udp.cpp");
exit(EXIT_FAILURE);
}
string str(readMsg); //Convert char array to string
str = str.substr(0, readSize); //Make sure the string is the length of readsize
return str;
}
void UDP::writeUDP(string message)
{
//Make char array
int writeSize = message.size();
char writeMsg[writeSize + 1] = {'[=11=]'};
//Convert string message to char array
for (int i = 0; i < writeSize; i++)
{
writeMsg[i] = message[i];
}
if ((sendto(fdSocketUDP_, writeMsg, writeSize, 0, (const struct sockaddr *)&ClientAddress_, (socklen_t)ClientAddressLength_)) < 0) //Send data via UDP protocol
{
perror("Error - sendto - udp.cpp");
exit(EXIT_FAILURE);
}
}
然后我有一台 windows 10 笔记本电脑,运行ning 一个 Labview 程序,该程序也通过 UDP 接收和发送数据。笔记本电脑设置为 UDP 客户端。以下是 Labview 中 UDP 设置的一些示例。
图片1(打开UDP连接):
图2(关闭UDP连接):
图 3(在 Labview 中读写 UDP):
以上,笔记本电脑上的Labview程序向树莓派发送3(“103”)+37(未显示)字节数据,然后从树莓派接收16字节数据。
笔记本电脑和树莓派通过本地网络上的 LAN 电缆连接。 RPi 使用 IP 地址 10.10.10.10 和端口 8080,笔记本电脑使用 IP 地址 10.10.10.1 和端口 1000。
下面是 Wireshark 测量,它测量 RPi 和笔记本电脑之间不同的发送和接收命令之间的时间。
图 4(wireshark 测量):
RPi 使用 "Len=3" 来确定 C++ 代码中 运行 的功能。 "Len=52" 和 "Len=37" 是从笔记本电脑 (Labview) 发送到 RPi(C++ 代码)的数据。 "Len=16" 是从树莓派发送到笔记本电脑的数据。
笔记本电脑首先向树莓派发送3+52字节的数据(客户端向服务器发送数据)。然后笔记本电脑向 RPi 发送 3+37 字节的数据(客户端向服务器发送数据)。 RPi 然后将 16 个字节的数据发送回笔记本电脑(服务器将数据发送到客户端)......等等。
一条命令(3+52 字节或 3+37+16 字节)大约需要 8 毫秒才能完成,每条命令之间的延迟(平均)约为 2 毫秒。如您所见,RPi 和笔记本电脑之间的数据大小 "relatively" 很小(3/37/52 字节)。
现在我的问题是: 有时命令之间会有约 20 毫秒的延迟(比平均约 2 毫秒长 10 倍),我不知道为什么。 ..(这在图 4 中用红点显示)。这种延迟通常发生在 RPi(UDP 服务器)向笔记本电脑(UDP 客户端 - 16 字节数据)发送数据之后,但它可能发生在不同的地方,如图 4 所示(在笔记本电脑向 RPi 发送 52 字节之后) ).我认为它与UDP有关,也许与设置有关,也许与ARP有关,但我不知道。我试过对 RPi 超频,调整 RPi 上 C++ 程序的优先级,调整 C++ 代码,但这似乎不是瓶颈。
这就像笔记本电脑和树莓派之间的 UDP 连接有时是 "lost" 或 "paused",然后需要一些时间才能使连接恢复正常。
我找到了解决问题的办法。为了解决长时间延迟,我不得不降低 UDP 读取缓冲区,因为我只通过 UDP 发送小包。
为此,我格式化了 RPi 上的 sysctl.conf 文件,该文件位于 /etc 文件夹中。 我添加了以下行:
net.core.rmem_default = 4096
net.core.rmem_max = 4096