3

標準的な例の「エコー」クライアント/サーバー アプリケーションをできるだけ速く実行しようとしていますが、ネットワークが制限要因になっていると確信しています。1 ギガビットのネットワーク カードを使用していますが、リソース モニターを使用すると、クライアントから 7 メガビットしか取得できません。

ソケット、メッセージ フレーミング、および長さインジケータの基本を理解し、長さインジケータによって示されるすべてのバイトを受信します。キープアライブ パケット、ハーフ オープン接続など。

ストック標準のソケット操作を使用して開始し、次に非同期の使用に切り替えました。(送信を非同期に変更しなかったのは、[知識があるように見える人が影響を与えるべきではないと誰かが言った] ためです) 私はまったく同じパフォーマンスを持っています。同じスレッドで行われます。しかし、私の簡単なテストでは、ループ送信で連続してスピンする 1 つのスレッドと、受信する別のまったく別のスレッドを専用にしました。

私はすべてを試しましたが、どこでより多くのパフォーマンスを得ることができるかについて完全に迷っています. 私は IPerf を使用し、毎秒 1 ギガビットのバック速度を報告し、リソース モニターも帯域幅を食いつぶしていることを示しました。

誰かがより完全な例の方向に私を向けることができたとしても。私が遭遇したもののほとんどは、取るに足らないものか不完全なものです。

これが一般的なコードです。

class Program
{
private static Socket sock;
private static BlockingCollection<string> queue;
private static int bytesReceived;
private static byte[] dataBuffer;
private static readonly byte[] lengthBuffer = new byte[4];

private static byte[] PrependLengthIndicator(byte[] data)
{
    return BitConverter.GetBytes(data.Length).Concat(data).ToArray();
}

private static void Receive()
{
    if (dataBuffer == null)
    {
        sock.BeginReceive(lengthBuffer, 0, 4, SocketFlags.None, ReceiveCallback, null);
    }
    else
    {
        sock.BeginReceive(dataBuffer, 0, bytesReceived, SocketFlags.None, ReceiveCallback, null);
    }
}

private static void ReceiveCallback(IAsyncResult ar)
{
    bytesReceived += sock.EndReceive(ar);
    if (dataBuffer == null)
    {
        // Currently receiving length indicator
        if (bytesReceived >= 4)
        {
            var length = BitConverter.ToInt32(lengthBuffer, 0);
            dataBuffer = new byte[length];
            bytesReceived = 0;
        }
    }
    else
    {
        if (bytesReceived == dataBuffer.Length)
        {
            // Finished reading
            var request = Encoding.ASCII.GetString(dataBuffer);
            dataBuffer = null;
            bytesReceived = 0;
            queue.Add(request);
        }
    }
    ContinueReading();
}

private static void ContinueReading()
{
    // Read into the appropriate buffer: length or data
    if (dataBuffer != null)
    {
        sock.BeginReceive(dataBuffer, bytesReceived, dataBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
    }
    else
    {
        sock.BeginReceive(lengthBuffer, bytesReceived, lengthBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
    }
}

}

サーバー部分は次のとおりです。

static void Main(string[] args)
{
        var listenSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listenSock.Bind(new IPEndPoint(IPAddress.Parse(ConfigurationManager.AppSettings["LocalIp"]), 3333));
        listenSock.Listen(10);
        Console.WriteLine("Server started...");
        sock = listenSock.Accept();
        Console.WriteLine("Connection accepted.");

        queue = new BlockingCollection<string>();
        Receive();
        var count = 0;
        var sender = new Thread(() =>
            {
                while (true)
                {
                    var bar = queue.Take() + "Resp";
                    count++;
                    var resp = Encoding.ASCII.GetBytes(bar);
                    var toSend = PrependLengthIndicator(resp);
                    if (count % 10000 == 0)
                    {
                        Console.WriteLine(bar);
                    }
                    sock.Send(toSend);
                }
            });
        sender.Start();
    }

クライアント部分は次のとおりです。

static void Main(string[] args)
{

    sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Console.WriteLine("Connecting...");
        sock.Connect(IPAddress.Parse(ConfigurationManager.AppSettings["EndPointIp"]), 3333);
        Console.WriteLine("Connected.");
        Receive();

        var count = 0;
        while(true)
        {
            count++;
            var foo = "Echo-" + count;
            var data = Encoding.ASCII.GetBytes(foo);
            var toSend = PrependLengthIndicator(data);
            sock.Send(toSend);
        }
    }
4

1 に答える 1

5

あなたは小さなメッセージを送っています。1 ギガビット/秒のリンクを飽和させるには、何百万個必要かを考えてみてください。ソケットへの各呼び出しは、CPU を焼き尽くします。

より大きなメッセージを送信します。また、常に新しいバッファを割り当てないようにしてください。Enumerable.Concatバイト バッファを連結するために使用しないでください。事前に割り当てられた配列で使用Array.Copyします。

使用しているスレッドが非常に少ない場合は、同期 IOに切り替えると高速になります (実際には、オーバーヘッドが少なくなります)。

この答えが正しいことは、64KB のバッファーを常に同期的に送信するだけの馬鹿げた無限送信ループを実行することで確認できます。リンクを飽和させます。

于 2013-01-03T14:19:15.830 に答える