gpt4 book ai didi

c++ - 发送 UDP 数据包的长时间延迟

转载 作者:可可西里 更新时间:2023-11-01 12:39:17 26 4
gpt4 key购买 nike

我有一个接收、处理和传输 UDP 数据包的应用程序。

如果接收和传输的端口号不同,一切正常。

如果端口号相同而 IP 地址不同,则通常可以正常工作,除非 IP 地址与运行应用程序的计算机位于同一子网中。在后一种情况下,send_to 函数需要几秒钟才能完成,而不是通常的几毫秒。

Rx Port  Tx IP          Tx Port    Result

5001 Same 5002 OK Delay ~ 0.001 secs
subnet

5001 Different 5001 OK Delay ~ 0.001 secs
subnet

5001 Same 5001 Fails Delay > 2 secs
subnet

这是一个演示问题的简短程序。

#include <ctime>
#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::udp;
using std::cout;
using std::endl;

int test( const std::string& output_IP)
{
try
{
unsigned short prev_seq_no;

boost::asio::io_service io_service;

// build the input socket

/* This is connected to a UDP client that is running continuously
sending messages that include an incrementing sequence number
*/

const int input_port = 5001;
udp::socket input_socket(io_service, udp::endpoint(udp::v4(), input_port ));

// build the output socket

const std::string output_Port = "5001";
udp::resolver resolver(io_service);
udp::resolver::query query(udp::v4(), output_IP, output_Port );
udp::endpoint output_endpoint = *resolver.resolve(query);
udp::socket output_socket( io_service );
output_socket.open(udp::v4());

// double output buffer size
boost::asio::socket_base::send_buffer_size option( 8192 * 2 );
output_socket.set_option(option);

cout << "TX to " << output_endpoint.address() << ":" << output_endpoint.port() << endl;



int count = 0;
for (;;)
{
// receive packet
unsigned short recv_buf[ 20000 ];
udp::endpoint remote_endpoint;
boost::system::error_code error;
int bytes_received = input_socket.receive_from(boost::asio::buffer(recv_buf,20000),
remote_endpoint, 0, error);

if (error && error != boost::asio::error::message_size)
throw boost::system::system_error(error);

// start timer
__int64 TimeStart;
QueryPerformanceCounter( (LARGE_INTEGER *)&TimeStart );

// send onwards
boost::system::error_code ignored_error;
output_socket.send_to(
boost::asio::buffer(recv_buf,bytes_received),
output_endpoint, 0, ignored_error);

// stop time and display tx time
__int64 TimeEnd;
QueryPerformanceCounter( (LARGE_INTEGER *)&TimeEnd );
__int64 f;
QueryPerformanceFrequency( (LARGE_INTEGER *)&f );
cout << "Send time secs " << (double) ( TimeEnd - TimeStart ) / (double) f << endl;

// stop after loops
if( count++ > 10 )
break;
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}

}
int main( )
{

test( "193.168.1.200" );

test( "192.168.1.200" );

return 0;
}

当在地址为 192.168.1.101 的机器上运行时,此程序的输出

TX to 193.168.1.200:5001
Send time secs 0.0232749
Send time secs 0.00541566
Send time secs 0.00924535
Send time secs 0.00449014
Send time secs 0.00616714
Send time secs 0.0199299
Send time secs 0.00746081
Send time secs 0.000157972
Send time secs 0.000246775
Send time secs 0.00775578
Send time secs 0.00477618
Send time secs 0.0187321
TX to 192.168.1.200:5001
Send time secs 1.39485
Send time secs 3.00026
Send time secs 3.00104
Send time secs 0.00025927
Send time secs 3.00163
Send time secs 2.99895
Send time secs 6.64908e-005
Send time secs 2.99864
Send time secs 2.98798
Send time secs 3.00001
Send time secs 3.00124
Send time secs 9.86207e-005

为什么会这样?有什么办法可以减少延迟吗?

注意事项:

  • 使用 code::blocks 构建,可在各种 Windows 操作系统下运行

  • 数据包长度为 10000 字节

  • 如果我将运行应用程序的计算机连接到第二个网络,问题就会消失。例如 WWLAN(蜂窝网络“火箭棒”)

据我所知,这是我们的情况:

这有效(不同的端口,相同的 LAN):

enter image description here

这也有效(相同的端口,不同的 LANS):

enter image description here

这不起作用(相同的端口,相同的 LAN):

enter image description here

这似乎可行(相同的端口、相同的 LAN、双宿主 Module2 主机)

enter image description here

最佳答案

鉴于在 Windows 上观察到大型数据报的目标地址与发送方在同一子网内不存在的对等方,问题可能是 send() 阻塞等待的结果对于 Address Resolution Protocol (ARP) 响应以便可以填充第 2 层以太网帧:

  • 发送数据时,第 2 层以太网帧将填充路由中下一跃点的媒体访问控制 (MAC) 地址。如果发送方不知道下一跳的 MAC 地址,它将广播 ARP 请求并缓存响应。使用发件人的子网掩码和目标地址,发件人可以确定下一跳是否与发件人在同一子网上,或者数据是否必须通过默认网关路由。根据问题中的结果,发送大型数据报时:

    • 发往不同子网的数据报没有延迟,因为默认网关的 MAC 地址在发送方的 ARP 缓存中
    • 发往发送方子网上不存在的对等点的数据报会导致等待 ARP 解析的延迟
  • 套接字的send buffer size (SO_SNDBUF) 被设置为 16384 字节,但发送的数据报的大小为 10000。缓冲区饱和时send()的行为没有具体说明,但有些系统会观察到send()阻塞。在这种情况下,如果任何数据报产生延迟(例如等待 ARP 响应),饱和将很快发生。

    // Datagrams being sent are 10000 bytes, but the socket buffer is 16384.
    boost::asio::socket_base::send_buffer_size option(8192 * 2);
    output_socket.set_option(option);

    考虑让内核管理套接字缓冲区大小或根据您的预期吞吐量增加它。

  • 当发送的数据报大小超过 Window 的注册表 FastSendDatagramThreshold‌ 参数时,send() 调用会阻塞,直到数据报发送完毕。有关详细信息,请参阅 Microsoft TCP/IP Implementation Details :

    Datagrams smaller than the value of this parameter go through the fast I/O path or are buffered on send. Larger ones are held until the datagram is actually sent. The default value was found by testing to be the best overall value for performance. Fast I/O means copying data and bypassing the I/O subsystem, instead of mapping memory and going through the I/O subsystem. This is advantageous for small amounts of data. Changing this value is not generally recommended.

如果观察到每个 send() 到发送方子网上的 existing peer 的延迟,则配置和分析网络:

  • 使用iperf衡量网络潜在吞吐量
  • 使用wireshark更深入地了解给定节点上发生的事情。查找 ARP 请求和响应。
  • 从发件人的机器上,ping 对等点,然后检查 APR 缓存。验证是否存在对等方的缓存条目并且它是正确的。
  • 尝试不同的端口和/或 TCP。这有助于确定网络策略是否正在限制或调整特定端口或协议(protocol)的流量。

另请注意,在等待 ARP 解析时快速连续发送低于 FastSendDatagramThreshold 值的数据报可能会导致数据报被丢弃:

ARP queues only one outbound IP datagram for a specified destination address while that IP address is being resolved to a media access control address. If a User Datagram Protocol (UDP)-based application sends multiple IP datagrams to a single destination address without any pauses between them, some of the datagrams may be dropped if there is no ARP cache entry already present. An application can compensate for this by calling the iphlpapi.dll routine SendArp() to establish an ARP cache entry, before sending the stream of packets.

关于c++ - 发送 UDP 数据包的长时间延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33875210/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com