2

さまざまなクライアントからUDPパケットを受信し、それらを他のクライアントに転送する必要があるUDPサーバーを作成しています。私はC#を使用しているので、各UDPソケットは私にとってUdpClientです。2つのUdpClientオブジェクトが必要だと思います。1つは受信用、もう1つは送信用です。受信ソケットは既知のポートにバインドされ、送信者はまったくバインドされません。

サーバーは各パケットを取得し、パケットデータでユーザー名を検索し、サーバーが維持しているルーティングリストに基づいて、パケットを1つ以上の他のクライアントに転送します。

リスニングUdpClientを次のコマンドで開始します。

   UdpClient udpListener = new UdpClient(new IPEndPoint(ListenerIP, UdpListenerPort));
   udpListener.BeginReceive(new AsyncCallback(OnUDPRead), udpListener);

私のコールバックは次のようになります:

    void OnUDPRead(IAsyncResult ar)
    {
        UdpClient udpListener = (UdpClient)ar.AsyncState;

        try
        {
            IPEndPoint remoteEndPoint = null;
            byte[] packet = udpListener.EndReceive(ar, ref remoteEndPoint);

            // Get connection based on data in packet

            // Get connections routing table (from memory)

            // ***Send to each client in routing table***
        }
        catch (Exception ex)
        {
        }

        udpListener.BeginReceive(new AsyncCallback(OnUDPRead), udpListener);
    }

サーバーは1秒あたり数百のパケットを処理する必要があります(これはVOIP用です)。私がよくわからない主な部分は、上記の「各クライアントに送信」の部分で何をするかです。または使用する必要がありますUdpClient.SendUdpClient.BeginSend?これはリアルタイムプロトコル用であるため、特定のクライアントに対して複数の送信を保留/バッファリングすることは意味がありません。

一度に投稿されるBeginReceive()は1つだけなので、OnUDPRead関数がすでに実行されている間は呼び出されないと思います。UdpClientがこの領域でTcpClientとは異なる動作をしない限り?私はそれに単一のバッファーを与えないので(私はそれにバッファーを与えません)、1つのBeginReceiveが投稿されただけでOnUDPReadを数回起動できると思いますか?

4

4 に答える 4

3

簡単な方法で1つのソケットを使用できない理由はわかりません。非同期I/Oは、この状況で驚くべき利点を提供するようには思えません。ノンブロッキングモードを使用するだけです。パケットを受信して​​送信します。send()によってEAGAINやEWOULDBLOCK、またはそのC#の表現が何であれ、それを削除するだけです。

プランでは、send()を最初に呼び出したときに、送信ソケットが自動的にバインドされます。クライアントはそのポートに応答します。したがって、それはあなたが読んでいるポートである必要があります。そして、あなたはすでにそれらの1つを持っています。

于 2012-05-29T23:49:56.460 に答える
2

これは規模に大きく依存します。つまり、中継する同時RTPストリームの数です。私は、SIPスイッチのメディアゲートウェイの一部として、まさにこのソリューションを開発しました。最初の予約にもかかわらず、C#を使用して非常に効率的に機能しています(単一サーバーで1000の同時ストリーム、20ミリ秒のPTimeを使用して1秒あたり50Kのデータグラム)。

試行錯誤の末、私は次のことを決定しました。

1)各ストリームに個別のスレッドを必要としないため、Socket.ReceiveFromAsyncを使用する方が同期アプローチを使用するよりもはるかに優れています。パケットが受信されると、スレッドプールを介してスケジュールされます。

2)SocketAsyncEventArgs構造体の作成/破棄には時間がかかります。起動時に構造体のプールを割り当て、プールからそれらを「借用」する方が適切です。

3)処理中に必要な他のオブジェクト(RTPPacketクラスなど)についても同様です。これらをこのように高速で動的に割り当てると、あらゆる種類の実際の負荷でGCの問題が非常に迅速に発生します。すべての動的割り当てを回避し、代わりに自分で管理するオブジェクトのプールを使用すると、この問題は解消されます。

4)入力ストリームと出力ストリームを調整するためにクロッキング、トランスコーディング、ミキシング、またはファイル再生を行う必要がある場合は、標準の.NETタイマーを使用しないでください。私はwinmm.dllからWin32マルチメディアタイマーをラップしました-これらははるかに正確でした。

最終的に、コンポーネントがIRTPSourceおよびIRTPSinkインターフェースを公開し、それらを接続して必要なグラフを作成できるアーキテクチャーになりました。RTP入出力コンポーネント、ミキサー、プレーヤー、レコーダー、(単純な)トランスコーダー、プレイアウトバッファーなどを作成し、これら2つのインターフェイスを使用してそれらを接続しました。次に、RTPパケットは、MMタイマーを使用してグラフを介してクロックされます。

C#は、CまたはC++を使用して通常アプローチされるものに適した選択肢であることが証明されました。

マイク

于 2012-06-06T23:46:53.527 に答える
0

EJPが言ったこと。

非同期IOを使用したソケットサーバーのスケーリングに関するほとんどすべての文献は、TCPシナリオを念頭に置いて書かれています。昨年、UDPソケットサーバーをスケーリングする方法について多くの調査を行いましたが、IOCP/epollはTCPほどUDPには適切ではないことがわかりました。こちらの回答をご覧ください

したがって、実際には、非同期I / Oを実行するよりも、複数のスレッドを使用してUDPソケットサーバーをスケーリングする方が非常に簡単です。そして、多くの場合、recvfrom/sendto呼び出しをポンピングする1つのスレッドで十分です。

サーバーには、クライアントごとに1つの同期ソケットがあることをお勧めします。次に、複数のスレッドにSocket.Select呼び出しをクライアントソケットの別のサブセットで実行させます。すべてのクライアントに対して単一のソケットを使用できると思いますが、そのソケットで複数のスレッドからオーディオパケットを処理しようとすると、順序が乱れる問題が発生します。

また、UdpClientの代わりに下位レベルのソケットクラスを使用することを検討してください。

ただし、本当にスケールアウトしたい場合は、C /C++でコア作業を実行することをお勧めします。C#では、winsockの上に抽象化レイヤーを追加すると、バッファーコピーが増え、レイテンシーが高くなります。winsockを使用して、バッファコピーを発生させずに、あるソケットから別のソケットにパケットを転送できる手法があります。

于 2012-05-30T01:15:17.587 に答える
0

良い設計は、車輪の再発明をしないことです:)

すでに多くのRTPメディアプロキシソリューションがあります:Kamailio、FreeSWITCH、Asterisk、および他のいくつかのオープンソースプロジェクト。私はFreeSWITCHの使用経験が豊富で、SIP通話のメディアプロキシとして使用することを強くお勧めします。

シグナリングプロトコルがSIPでない場合は、他のプロトコルにもいくつかのソリューションがあります。

于 2012-06-16T20:50:00.090 に答える