接收UDP消息而无需分配新的缓冲区

发布于 2025-01-23 21:44:05 字数 320 浏览 0 评论 0原文

尝试使用udpclient.receive时,我注意到它只是返回字节数组。这是否为每个数据包分配一个新的缓冲区?据我所知,为每个收到的数据包分配一个新的字节阵列对性能和内存使用情况不利。为了在使用TCP时解决同样的问题,我使用Arraypool租用缓冲区。但是我该如何使用UDP做到这一点?

我考虑过使用相同的方法(例如使用TCP),只是使用插座,但是UDP不是基于流的,因此方法毫无意义(我不知道一个数据包将是多大的,我无法从类似的流中继续阅读在TCP中)。

tl; dr:

在接收UDP数据包时,我如何保存性能?

When trying to use UdpClient.Receive I noticed that it just returns a byte array. Does this allocate a new buffer for every packet? As far as I know, allocating a new byte array for every received packet is bad for performance and memory usage. To get around that same problem when using Tcp, I used an ArrayPool to rent the buffers. But how would I do that with Udp?

I thought about using the same method like with Tcp and just using a socket, but udp is not stream based and therefore that approach makes little sense (I don't know how big one packet will be and I cannot continually read from a stream like in Tcp).

TL;DR:

How do I save performance when receiving UDP packets while still always receiving the full packet?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浮生未歇 2025-01-30 21:44:05

尝试使用udpclient.receive时,我注意到它只是返回字节数组。这是否为每个数据包分配一个新的缓冲区?

通常,是的。

在内部,udpclient使用固定尺寸的字节[65536]数组来读取每个数据包。如果接收到的数据包正是如此大小,则将内部缓冲区按原样返回。但是,如果接收到的数据包小于该大小,通常是这种情况,则将其分配一个新的byte []的实际数据包大小,将内部缓冲区的数据复制到该新数组中,并且然后返回。

我不知道一包有多大

可以确定下一个数据包的实际尺寸而不从插座队列中提取数据包。 WinSock的recvFrom()功能具有MSG_PEEK为此目的标志。

udpclient.receive()不支持该标志,但是udpclient的基础socket在其rechodffrom()方法,例如:

byte[] ignore = new byte[0]; // the buffer parameter of ReceivFrom() can't be null!
EndPoint ep;

int size = UdpClient.Client.ReceiveFrom(ignore, SocketFlags.Peek, ref ep);
// obtain a buffer of sufficient size ... 
UdpClient.Client.ReceiveFrom(buffer, size, SocketFlags.None, ref ep);

尽管socket.receivefrom()的确允许0长度byte [] array(它将传递空指针和0个缓冲区长度到Winsock的<代码> recvFrom()),我不确定的是socket.receivefrom()实际上会返回下一个数据包大小,或者是否会抛出socketExecception如果您尝试使用byte []数组小于实际数据包(我不确定recvfrom() byte []数组>报告使用msg_peek flag使用 wsaemsgsize 错误。

为了避免这个潜在的问题,请继续阅读...

在仍然始终收到完整数据包的同时,在接收UDP数据包时如何保存性能?

我建议您只尝试使用单个64K byte [] array(例如udpclient在内部使用),因为UDP数据包永远不会超过该大小。只需直接使用udpclient.client.client.receivefrom()而不是使用udpclient.receive(),例如:

byte[] myBuffer = new byte[65536];
EndPoint ep;

...

int received = UdpClient.Client.ReceiveFrom(myBuffer, ref ep);
// use myBuffer up to received bytes ...
// use (IPEndPoint)ep if needed ...

... 

这样,您可以重复使用单个byte [ ]多个读数的数组(就像udpclient在内部使用)。

When trying to use UdpClient.Receive I noticed that it just returns a byte array. Does this allocate a new buffer for every packet?

Typically, yes.

Internally, UdpClient uses a fixed-sized byte[65536] array to read each packet. If a received packet is exactly that size, the internal buffer is returned as-is. However, if the received packet is less than that size, which is usually the case, then it allocates a new byte[] of the actual packet size, copies the internal buffer's data into that new array, and then returns it.

I don't know how big one packet will be

It is possible in the Winsock API to determine the next packet's actual size without extracting the packet from the socket's queue. Winsock's recvfrom() function has a MSG_PEEK flag for that purpose.

UdpClient.Receive() does not support that flag, however the UdpClient's underlying Socket does in its ReceiveFrom() method, eg:

byte[] ignore = new byte[0]; // the buffer parameter of ReceivFrom() can't be null!
EndPoint ep;

int size = UdpClient.Client.ReceiveFrom(ignore, SocketFlags.Peek, ref ep);
// obtain a buffer of sufficient size ... 
UdpClient.Client.ReceiveFrom(buffer, size, SocketFlags.None, ref ep);

Although Socket.ReceiveFrom() does allow a 0-length byte[] array (it will pass a NULL pointer and 0 buffer length to Winsock's recvfrom()), what I'm not sure about is whether Socket.ReceiveFrom() will actually return the next packet size, or if it will throw a SocketException if you try to peek into the queue using a byte[] array that is smaller than the actual packet (as I am not sure whether or not recvfrom() reports a WSAEMSGSIZE error when the MSG_PEEK flag is used).

To avoid this potential issue, keep reading ...

How do I save performance when receiving UDP packets while still always receiving the full packet?

Rather then trying to pool arrays, I would suggest you simply use a single 64K byte[] array (like UdpClient does internally), since UDP packets will never exceed that size. Simply use UdpClient.Client.ReceiveFrom() directly instead of using UdpClient.Receive(), eg:

byte[] myBuffer = new byte[65536];
EndPoint ep;

...

int received = UdpClient.Client.ReceiveFrom(myBuffer, ref ep);
// use myBuffer up to received bytes ...
// use (IPEndPoint)ep if needed ...

... 

This way, you can re-use a single byte[] array for multiple reads (just as UdpClient does internally).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文