代码之家  ›  专栏  ›  技术社区  ›  Fake Code Monkey Rashid

使用Begin*End*方法时,在C#中处理数据包碎片的工作示例是什么样的?

  •  2
  • Fake Code Monkey Rashid  · 技术社区  · 17 年前

    在充分使用异步套接字编程之后,我注意到服务器正在接收分块的有效负载(即:同一缓冲区中有多个完整的有效负载)。因此,我提出了以下建议:

    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.
        }
    }
    

    ***我甚至不知道该怎么做,我想看看代码。我有一个想法,它涉及到将已完成的有效负载复制到缓冲区的开头,然后从那里提取。

    http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx <-但我认为我的总体问题仍然适用)。

    我正在寻找两个问题的答案,即:

    1. 这个方法是正确的还是正确的 遗漏了什么?
    2. 工作示例是什么 处理网络中的数据包碎片 看起来像什么?

    编辑:我正在使用原始套接字。

    提前感谢你的帮助。

    编辑:约翰·桑德斯(John Saunders)和格雷格·休吉尔(Greg Hewgill)提出了将数据视为一个流的观点,但这并没有为我提供一个具体的示例,说明如何处理最后一个分块的有效负载,有时它是碎片化的。

    编辑:我读过乔恩·斯基特的答案 here 这与我看到的其他答案基本上是一样的,但这对我没有多大帮助,因为我已经知道我必须做什么,但不知道如何去做。

    编辑:为了详细说明碎片化的含义,请考虑下面的接收缓冲区:

    • 224TEST3foo3bar
    • 224TEST3foo3bar224TEST3foo3bar
    • 224TEST3foo3bar224TEST3foo
    • 3bar 224test3foo3bar

    编辑:我找到了 this 和 this 这让我 here Vadym Stetsiak 几乎把一切都弄清楚了(他的回答是我一直在寻找的答案之一)。

    2 回复  |  直到 9 年前
        1
  •  3
  •   John Saunders    17 年前

    这可能与碎片有关,也可能与碎片无关。


    下面的代码目前未经测试。我想在编写它的服务器端并测试两者之前先发布它。

    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);
    }
    

    请注意,接收到的消息长度是错误的。套接字层可以一次给我一个字节。作为重构的一部分,我将重新讨论这个问题,重构将把计数接收到一个单独的两字节缓冲区中,并将循环更改为一个do/while。

        2
  •  0
  •   Fake Code Monkey Rashid    17 年前

    当你必须自己做的时候,可以这样做( reference here ):

    /// 
    /// 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;
            }
        }
    }
    

    我自己并不是这样做的,但这很有帮助。