2

非同期ソケットプログラミングで十分に遊んだ後、サーバーがチャンク化されたペイロード(つまり、同じバッファーにある複数の完全なペイロード)を受信して​​いることに気付きました。だから私は次のことを思いついた:

if (bytes_to_read > 0)
{
    while (bytes_to_read > 0)

        // Get payload size as int.

        // Get payload in byte format.

        // Do something with payload.

        // Decrease the amount of bytes to read.
    }

    // Wait for more data.
}

そして、パケットの断片化に気づきました(つまり、完全なペイロードが一緒にチャンク化されていると思っていたのは必ずしもそうではありませんでした)。これにより、前のコードが次のように変更されました。

if (bytes_to_read > 0)
{
    while (bytes_to_read > 0)
    {
        // Get payload size as int.

        // Check if the payload size is less than or equal to the amount of bytes left to read.
        if (payload_size <= bytes_to_read)
        {
            // Get payload in byte format.

            // Do something with payload.

            // Decrease the amount of bytes to read.
        }
        else
        {
            // We received a fragmented payload.
            break;
        }
    }

    if (bytes_to_read == 0)
    {
        // Wait for more data.
    }
    else if (bytes_to_read > 0)
    {
        // Wait for more data where we left off. ***
    }
    else
    {
        // Something awful happened.
    }
}

***これについてどうすればいいのかさえわからないので、そのためのコードを見たいと思います。未完成のペイロードをバッファの先頭にコピーし、そこからピックアップする必要があると思いました。

私が含めた擬似コードは、私が使用しているBegin * End *メソッドに基づいています(ここにある*Asyncセットのメソッドを使用する必要があることを認識しています-> http://msdn.microsoft.com/en-us /library/system.net.sockets.socketasynceventargs.aspx <-しかし、私の全体的な質問はまだ当てはまると思います)。

私は2つの質問に対する答えを探しています-すなわち:

  1. このアプローチは正しいですか、それとも何かが足りませんか?
  2. C#でパケットの断片化を処理する実際の例はどのように見えますか?

編集:私はrawソケットを使用しています。

よろしくお願いします。

編集:JohnSaundersとGregHewgillは、データをストリームとして扱うという点を指摘しましたが、それは、断片化されることがある最後のチャンク化されたペイロードを処理する方法の具体的な例を提供しません。

編集:私はここでジョン・スキートの答えを読みました。これは基本的に私が見た他の答えと同じ線に沿っていますが、私がしなければならないことはすでに得ていますが、それを行う方法はわかっていないので、あまり役に立ちません。

編集:断片化の意味を詳しく説明するには、次の受信バッファーを検討してください。

  • 224TEST3foo3bar
  • 224TEST3foo3bar224TEST3foo3bar
  • 224TEST3foo3bar224TEST3foo
  • 3bar224TEST3foo3bar

編集:私はこれこれが私をここに導くことを見つけました。Vadym Stetsiakはほぼすべてをクリアしました(彼は私が探していた答えの1つでした)。

4

2 に答える 2

3

これは、断片化と関係がある場合とない場合があります。

一般に、ソケットは一度に好きなだけ多くのバイトを渡します。あなたの仕事は、メッセージ全体のバイト数を把握し、それらをすべて読み取ることです。必要なバイトがすべて揃うまで、または例外が発生するまでループを続けます。


次のコードは現在テストされていません。サーバー側を作成して両方をテストする前に、投稿したいと思いました。

private static string ReceiveMessage(Socket socket)
{
    const int BUFFER_SIZE = 1024;
    var inputBuffer = new byte[BUFFER_SIZE];
    var offset = 0;
    var bytesReceived = socket.Receive(
        inputBuffer, offset, BUFFER_SIZE - offset, SocketFlags.None);
    if (bytesReceived < 2)
    {
        throw new InvalidOperationException("Receive error");
    }

    var inputMessageLength = inputBuffer[0]*256 + inputBuffer[1];
    offset += bytesReceived;
    var totalBytesReceived = bytesReceived;
    while (bytesReceived > 0 &&
           totalBytesReceived < inputMessageLength + 2)
    {
        bytesReceived = socket.Receive(
            inputBuffer, offset, BUFFER_SIZE - offset, SocketFlags.None);
        offset += bytesReceived;
        totalBytesReceived += bytesReceived;
    }

    return Encoding.UTF8.GetString(
        inputBuffer, 2, totalBytesReceived - 2);
}

メッセージ長の受信が間違っていることに注意してください。ソケット層は、一度に 1 バイトずつ提供できます。カウントを別の 2 バイト バッファーに受け取り、ループを単一の do/while に変更するリファクタリングの一環として、これを再検討します。

于 2009-07-15T01:57:01.733 に答える
0

自分でやらなければならない場合は、次のように行うことができます (ここを参照):

/// 
/// Server state holds current state of the client socket
///
class AsyncServerState
{
   public byte[] Buffer = new byte[512]; //buffer for network i/o
   public int DataSize = 0; //data size to be received by the server

   //flag that indicates whether prefix was received
   public bool DataSizeReceived = false;

   public MemoryStream Data = new MemoryStream(); //place where data is stored
   public SocketAsyncEventArgs ReadEventArgs = new SocketAsyncEventArgs();
   public Socket Client;
}

/// 
/// Implements server receive logic
/// 
private void ProcessReceive(SocketAsyncEventArgs e)
{
    //single message can be received using several receive operation
    AsyncServerState state = e.UserToken as AsyncServerState;

    if (e.BytesTransferred <= 0 || e.SocketError != SocketError.Success)
    {
        CloseConnection(e);
    }

    int dataRead = e.BytesTransferred;
    int dataOffset = 0;
    int restOfData = 0;

    while (dataRead > 0)
    {
        if (!state.DataSizeReceived)
        {
            //there is already some data in the buffer
            if (state.Data.Length > 0)
            {
                restOfData = PrefixSize - (int)state.Data.Length;
                state.Data.Write(state.Buffer, dataOffset, restOfData);
                dataRead -= restOfData;
                dataOffset += restOfData;
            }
            else if (dataRead >= PrefixSize)
            {   //store whole data size prefix
                state.Data.Write(state.Buffer, dataOffset, PrefixSize);
                dataRead -= PrefixSize;
                dataOffset += PrefixSize;
            }
            else
            {   // store only part of the size prefix
                state.Data.Write(state.Buffer, dataOffset, dataRead);
                dataOffset += dataRead;
                dataRead = 0;
            }

            if (state.Data.Length == PrefixSize)
            {   //we received data size prefix
                state.DataSize = BitConverter.ToInt32(state.Data.GetBuffer(), 0);
                state.DataSizeReceived = true;

                state.Data.Position = 0;
                state.Data.SetLength(0);
            }
            else
            {   //we received just part of the headers information
                //issue another read
                if (!state.Client.ReceiveAsync(state.ReadEventArgs))
                    ProcessReceive(state.ReadEventArgs);
                    return;
            }
        }

        //at this point we know the size of the pending data
        if ((state.Data.Length + dataRead) >= state.DataSize)
        {   //we have all the data for this message

            restOfData = state.DataSize - (int)state.Data.Length;

            state.Data.Write(state.Buffer, dataOffset, restOfData);
            Console.WriteLine("Data message received. Size: {0}",
                                  state.DataSize);

            dataOffset += restOfData;
            dataRead -= restOfData;

            state.Data.SetLength(0);
            state.Data.Position = 0;
            state.DataSizeReceived = false;
            state.DataSize = 0;

            if (dataRead == 0)
            {
                if (!state.Client.ReceiveAsync(state.ReadEventArgs))
                    ProcessReceive(state.ReadEventArgs);
                    return;
            }
            else
                continue;
        }
        else
        {   //there is still data pending, store what we've
            //received and issue another BeginReceive
            state.Data.Write(state.Buffer, dataOffset, dataRead);

            if (!state.Client.ReceiveAsync(state.ReadEventArgs))
                ProcessReceive(state.ReadEventArgs);

            dataRead = 0;
        }
    }
}

私は自分でこの方法を正確に行ったわけではありませんが、役に立ちました。

于 2009-07-30T06:32:47.510 に答える