SendToAsync 内存泄漏?

发布于 2024-09-10 20:05:44 字数 1808 浏览 8 评论 0原文

我有一个简单的 .net 3.5sp1 Windows 应用程序(C# 语言),充当 UDP 服务器。它监听端口,从端点接收数据,然后将接收到的数据重新传输到另一个端点(即中继,用于直播数据流)。我遇到的情况是,连接大约 20 分钟后,情况开始恶化。我还注意到它每秒消耗大约 50-100K 的内存,这些内存在 GC 后永远不会被释放。我必须关闭应用程序并重新启动它。不好。我已将问题范围缩小到以下代码,该代码会重新传输到另一端:

var sendBuffer = new byte[readCount];
Array.Copy(readData, sendBuffer, readCount);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.RemoteEndPoint = p.EP;
args.SetBuffer(sendBuffer, 0, sendBuffer.Length);
SwitchWindow.LineSocket.SendToAsync(args);

有人有 SendToAsync 内存泄漏的经验吗?

更新:

当套接字初始化时,我实例化一个状态对象(仅完成一次)。状态对象有一个名为“buffer”的属性,它是一个字节数组。我从套接字接收异步数据,如下所示:

private void beginReceiveData(ref MessageState state)
{
    var ipeSender = new IPEndPoint(IPAddress.Any, 0);
    var epSender = (EndPoint)ipeSender;

    state.workSocket = LineSocket;
    state.EP = epSender;
    state.workSocket.BeginReceiveFrom(state.buffer, 0, MessageState.BufferSize,
        SocketFlags.None, ref epSender,
        new AsyncCallback(ReceiveDataCB),
        state);
}

然后,在回调(ReceiveDataCB)中,我检索异步对象,然后将字节缓冲区传递给另一个函数进行处理,该函数又调用上面发布的代码进行重传另一侧(state.buffer 变为 readData)。

更新#2:

按照我的直觉,我将发送代码更改为以下内容,摆脱了 SocketAsyncEventArgs 和 SendToAsync:

var sendBuffer = new byte[readCount];
Array.Copy(readData, sendBuffer, readCount);
SwitchWindow.LineSocket.BeginSendTo(
    sendBuffer, 0, sendBuffer.Length, SocketFlags.None,
    p.EP, new AsyncCallback(echoCB), null);

当然,我添加了一个“echoCB”回调,它除了调用 EndSendTo 之外什么也不做。内存泄漏现在已经消失了!我怀疑这与创建如此多的 SocketAsyncEventArgs 对象以及挂在这些对象上的异步函数有关,每个数据包一个(每秒 33 个数据包,可以快速增加)。我再次查看了 SocketAsyncEventArgs 的 MSDN 文档,我注意到在提供的服务器“示例”代码中,他们使用了 SocketAsyncEventArgs 对象池。我认为它的设计并不是真正按照我使用它的方式工作。我认为重点是不必为每次调用实例化这些缓冲区等,因此可以重用它们并允许服务器获得更好的性能。

I have a simple .net 3.5sp1 windows application (in C#) that acts as a UDP server. It listens on a port, receives data from an endpoint, and then retransmits what it receives to another endpoint (i.e. a relay, for a live broadcast data stream). What I'm experiencing is after the connection is up for about 20 minutes, it starts to deteriorate. Also I notice that it is gobbling up about 50-100K of memory per second, which doesn't ever get released after GC. I have to close the app and restart it. Not good. I have narrowed down the problem to the following code, which does the retransmission to the other side:

var sendBuffer = new byte[readCount];
Array.Copy(readData, sendBuffer, readCount);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.RemoteEndPoint = p.EP;
args.SetBuffer(sendBuffer, 0, sendBuffer.Length);
SwitchWindow.LineSocket.SendToAsync(args);

Does anyone have any experience with memory leaks with SendToAsync?

UPDATE:

I instantiate a state object (only done once) when the socket is initialized. The state object has a property called "buffer", which is a byte array. I receive data async from the socket like such:

private void beginReceiveData(ref MessageState state)
{
    var ipeSender = new IPEndPoint(IPAddress.Any, 0);
    var epSender = (EndPoint)ipeSender;

    state.workSocket = LineSocket;
    state.EP = epSender;
    state.workSocket.BeginReceiveFrom(state.buffer, 0, MessageState.BufferSize,
        SocketFlags.None, ref epSender,
        new AsyncCallback(ReceiveDataCB),
        state);
}

And, then on my callback (ReceiveDataCB), I am retrieving the async object, and then passing the byte buffer to another function for processing, which in turn calls the code posted above for retransmission to the other side (state.buffer becomes readData).

UPDATE #2:

Following my gut, I changed the sending code to the following, getting rid of SocketAsyncEventArgs and SendToAsync:

var sendBuffer = new byte[readCount];
Array.Copy(readData, sendBuffer, readCount);
SwitchWindow.LineSocket.BeginSendTo(
    sendBuffer, 0, sendBuffer.Length, SocketFlags.None,
    p.EP, new AsyncCallback(echoCB), null);

And, of course, I added an "echoCB" callback that does nothing other than calling EndSendTo. The memory leak is now gone! I suspect it had something to do with creating so many SocketAsyncEventArgs objects, and the async function hanging onto these, one for each packet (at 33 packets per second, that can add up fast). I looked once again at the MSDN documentation for SocketAsyncEventArgs, and I noticed that on the server "example" code provided, they used a pool of SocketAsyncEventArgs objects. I don't think its really designed to work the way I was using it. I think the whole point is to not have to instantiate these buffers, etc, for each call, therefore reusing them and allowing the server better performance.

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

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

发布评论

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

评论(4

‘画卷フ 2024-09-17 20:05:44

您可能没有调用 SocketAsyncEventArgs.Dispose()

You are probably not calling SocketAsyncEventArgs.Dispose()

影子是时光的心 2024-09-17 20:05:44

readData 缓冲区分配在哪里?
整个接收/发送循环是否在同一范围内?

Where is the readData buffer allocated?
Is the whole receive/send loop inside the same scope?

疯狂的代价 2024-09-17 20:05:44

您始终可以用来

!gcroot <address>

追踪谁保留了您的对象的引用。请参阅 CLR 内存泄漏

You can always use

!gcroot <address>

to track down who's keeping a reference on your objects. See CLR Memory Leak.

归属感 2024-09-17 20:05:44

我认为您的中继节点在接收数据方面做了更多的工作,并且最终接收数据的速度比发送数据的速度要快。当数据从线路中传入时,您将为它分配内存并将其排队以异步方式发送,但是传出数据的发送速度可能比您接收(和分配)新数据的速度慢得多,从而导致过载传入的数据。下载速度高于上传速度的网络节点通常会出现这种情况。

您可以限制接受和分配的传入数据量。您可以使用回调来执行此操作 (args.Completed),方法是跟踪排队等待发送的数据量与实际发送的数据量(通过跟踪回调中发送的数据量)。如果等待发送的数据量大于某个任意限制,则不接受新数据。

如果您正在处理流视频,您可能必须跳过帧或丢弃整个数据包。

I think your relay node is doing much more work receiving data and is ultimately receiving data faster than it is sending. As data comes in off the wire, you are allocating memory for it and queuing that up to be sent async style, but your outgoing data may be sent much slower than you are receiving (and allocating for) new data, resulting in an overload of incoming data. This is often the case on network nodes that have higher download speeds than upload speeds.

You can throttle the amount of incoming data you accept and allocate for. You can use a callback to do this (args.Completed) by tracking the amount of data queued up to be sent versus actually sent (by tracking sent in the callback). If the amount of data waiting to be sent is larger than some arbitrary limit, don't accept new data.

If you are doing streaming video, you may have to skip frames or discard entire packets.

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