O que faz um exemplo de trabalho de lidar com a fragmentação de pacotes em C # olhar como quando se usa o * End * método Begin?

StackOverflow https://stackoverflow.com/questions/1129000

Pergunta

Depois de jogar bastante com soquete assíncrono programação notei que o servidor estava recebendo cargas fragmentada (ou seja: mais do que uma carga completa sentado no mesmo tampão). Então eu vim com o seguinte:

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

E então eu observei fragmentação de pacotes (ou seja: o que eu achava que eram cargas completas fragmentada juntos nem sempre foi assim) que alterou o código anterior para algo como:

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

*** Eu nem sei como ir sobre isso e gostaria de ver código para ele. Eu tinha uma idéia de que envolvia copiar a carga útil em concluído para o início do buffer e, em seguida, pegar a partir daí.

O código pseudo eu incluído é baseado no Begin * End * método que estou usando (Estou ciente de que eu deveria estar usando o * conjunto assíncrono de métodos encontrado aqui -> http://msdn.microsoft.com/en-us/library/system.net.sockets. socketasynceventargs.aspx <-. mas eu acho que a minha pergunta geral ainda é aplicável)

Estou buscando as respostas para perguntas 2 - a saber:

  1. É esta abordagem correta ou sou eu faltando alguma coisa?
  2. O que faz um exemplo de trabalho de lidar com a fragmentação de pacotes em C # parece?

EDIT:. Eu estou usando soquetes brutos

Agradecemos antecipadamente por sua ajuda.

EDIT:. John Saunders e Greg Hewgill ter trazido até o ponto de tratamento de dados como um fluxo, mas isso não me fornecer um exemplo concreto de como lidar com a última carga fragmentada, por vezes, ser fragmentado

EDIT: Eu li a resposta de Jon Skeet aqui que é basicamente ao longo das mesmas linhas como as outras respostas que tenho visto, mas isso não me ajudar tanto quanto eu já começa o que eu tenho que fazer, mas não como fazê-lo.

EDIT: Para elaborar sobre o que quero dizer por fragmentação, considere o seguinte o buffers de recepção:

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

EDIT: Eu encontrei este e este , que me levam aqui . Vadym Stetsiak cancelou quase tudo para cima (o seu foi uma das respostas que eu estava procurando).

Foi útil?

Solução 2

Quando você tem que fazê-lo sozinho, ele pode ser feito assim ( referência aqui ):

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

Eu não fazê-lo exatamente dessa forma me mas ajudou.

Outras dicas

Isto pode ou não pode ter nada a ver com a fragmentação.

Em geral, o socket irá passar por você como muitos bytes de cada vez como se sente. Seu trabalho é saber quantos bytes estão em sua mensagem global, e lê-los todos. Basta manter looping até que você tenha todos os bytes que você precisa, ou até que haja uma exceção.


O código a seguir não foi testado no momento. Eu pensei que eu ia postar-lo antes de escrever o lado do servidor do mesmo e testar ambos.

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

Note que o recebimento do comprimento da mensagem é errado. O socket layer poderia dar-me um byte de cada vez. Vou revisitar que, como parte de uma refatoração que receberá a contagem em um buffer de dois bytes separados, e mudar o circuito em um único do / while.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top