2

可能重复:
从套接字接收的流是否仅限于单个发送命令?

注意:我认为这个问题非常复杂(希望不是你们,这就是我在这里问的原因哈哈),我尽我所能尽可能简单明了地解释它。

在我的应用程序中,我不断在固定大小的缓冲区中接收字节数组。

我收到的这些字节数组系列已被“二进制”序列化。

但是,有时接收到的字节数组会大于固定大小的缓冲区,因此我需要将当前接收到的字节数组存储到容器中并再次循环以接收剩余的字节数组。

我现在的问题是如何“连接”或“组合”或“加入”我收到的所有“批次”字节数组(并存储在一个容器中,可能是一个字节数组队列)以形成一个单字节数组,然后反序列化它们?

int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
    {
        // There might be more data, so store the data received so far.
        // If the buffer was not filled, I have to get the number of bytes received as Thorsten Dittmar was saying, before queuing it
        dataReceivedQueue.Enqueue(state.buffer);

        // Get the rest of the data.
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
        new AsyncCallback(ReceiveCallback_onQuery), state);
    }
else
{
    // All the data has arrived; put it in response.
    response_onQueryHistory = ByteArrayToObject(functionThatCombinesBytes(dataReceivedQueue));

    // Signal that all bytes have been received.
    receiveDoneQuery.Set();
}

state.buffer 是接收数据的缓冲区。buffer 是一个大小为 4096 的字节数组。 state 是 StateObject 类型。

ByteArrayToObject(byte []) 负责反序列化接收到的数据并将其转换回其对象形式

functionThatCombinesBytes(Queue) 此函数将接收一个字节队列并将所有字节“组合”成一个字节数组

4

3 回答 3

3

仅仅因为您BeginReceive使用特定大小的缓冲区进行调用,并不意味着它一定会完全填满缓冲区,因此很可能您的一些排队缓冲区实际上只会部分填充接收到的数据,其余部分是零,如果您只是将它们连接在一起,这几乎肯定会破坏您的组合流,因为您还没有存储实际读入缓冲区的字节数。您似乎每次都在重用相同的缓冲区,因此您只会用新数据覆盖已读取的数据。

因此,我建议用 , 替换你dataReceivedQueueMemoryStream,并使用类似的东西:

if (bytesRead > 0)
    {
        // There might be more data, so store the data received so far.
        memoryStream.Write(state.buffer, 0, bytesRead);

        // Get the rest of the data.
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
        new AsyncCallback(ReceiveCallback_onQuery), state);
    }
else
{
    // All the data has arrived; put it in response.
    response_onQueryHistory = ByteArrayToObject(memoryStream.ToArray());

    // Signal that all bytes have been received.
    receiveDoneQuery.Set();
}
于 2012-12-07T12:57:45.383 回答
2

首先,除非您dataReceivedQueue的类型实现自己的(或覆盖QueueEnqueue方法,否则每次调用state.buffer都会重写您的方法。client.BeginReceive

您可以简单地MemoryStream向您的成员添加一个成员,StateObject并在它们到来时向其附加字节:

state.rawData.Seek(0, SeekOrigin.End);
state.rawData.Write(state.buffer, 0, bytesRead);
于 2012-12-07T13:10:00.783 回答
1

首先,您不仅需要存储字节数组,还需要存储数组中实际有效的字节数。例如,每次接收可能不会完全填满缓冲区,因此会返回字节数(bytesRead在您的代码中)。

如果你有这个,你可以通过总结每个“批次”接收到的字节数来计算最终缓冲区的大小。

之后,您可以 - 在循环中 - 使用Array.Copy将“批次”复制到具有指定长度的指定位置到目标数组中。

例如,这可能如下所示:

// Batch is a class that contains the batch byte buffer and the number of bytes valid
int destinationPos = 0;
byte[] destination = new byte[<number of bytes in total>];
foreach (Batch b in batches)
{
    Array.Copy(b.Bytes, 0, destination, destinationPos, b.ValidLength);
}
于 2012-12-07T12:56:11.753 回答