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?
-
13-09-2019 - |
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:
- É esta abordagem correta ou sou eu faltando alguma coisa?
- 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).
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.