2

UDP サーバーとして機能する単純な .net 3.5sp1 Windows アプリケーション (C#) があります。ポートでリッスンし、エンドポイントからデータを受信し、受信したデータを別のエンドポイント (つまり、ライブ ブロードキャスト データ ストリームのリレー) に再送信します。私が経験しているのは、接続が約 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);

そしてもちろん、EndSendTo を呼び出すだけの "echoCB" コールバックを追加しました。メモリリークがなくなりました!非常に多くの SocketAsyncEventArgs オブジェクトの作成と、これらにぶら下がっている非同期関数がパケットごとに 1 つ (1 秒あたり 33 パケットで、高速に加算される可能性があります) と関係があると思われます。SocketAsyncEventArgs の MSDN ドキュメントをもう一度見たところ、提供されたサーバーの「サンプル」コードで、SocketAsyncEventArgs オブジェクトのプールが使用されていることがわかりました。私が使っていたように機能するように設計されているとは思いません。要点は、呼び出しごとにこれらのバッファーなどをインスタンス化する必要がないため、それらを再利用してサーバーのパフォーマンスを向上させることだと思います。

4

4 に答える 4

5

おそらく SocketAsyncEventArgs.Dispose() を呼び出していません

于 2011-08-29T06:44:44.647 に答える
0

あなたのリレーノードはデータを受信するためにはるかに多くの仕事をしていて、最終的には送信するよりも速くデータを受信して​​いると思います。データがネットワークから受信されると、そのデータにメモリを割り当て、非同期スタイルで送信するためにキューに入れますが、送信データは、新しいデータを受信(および割り当て)するよりもはるかに遅く送信される可能性があり、その結果、着信データ。これは、アップロード速度よりもダウンロード速度が速いネットワークノードでよく見られます。

受け入れて割り当てる受信データの量を調整できます。コールバックを使用してこれを行うことができます(args.Completed)。これは、送信されるまでにキューに入れられたデータの量と実際に送信されたデータの量を追跡することによって(コールバックで送信されたデータを追跡することによって)行われます。送信を待機しているデータの量が任意の制限よりも多い場合は、新しいデータを受け入れないでください。

ビデオのストリーミングを行っている場合は、フレームをスキップするか、パケット全体を破棄する必要があります。

于 2010-07-19T18:28:02.490 に答える
0

バッファはどこにreadData割り当てられますか?
受信/送信ループ全体が同じスコープ内にあるか?

于 2010-07-19T18:13:45.880 に答える
0

いつも使えます

!gcroot <address>

オブジェクトの参照を保持しているユーザーを追跡します。CLR メモリ リークを参照してください。

于 2010-07-19T18:14:02.507 に答える