如果它不适合 TCP 缓冲区,我如何确保我收到整个消息?

How do I make sure I receive the whole message if it doesn't fit in the buffer in TCP?

我最近开始在 C# 中使用 TCP。我现在正处于我希望客户端接收服务器发送的数据的地步。

我知道不能保证客户端一次接收到所有数据。如果发送的数据大小大于客户端缓冲区的大小,则数据将被分段发送。所以我的问题是:如何将所有接收到的数据存储在字节数组中,然后在接收到所有数据后将其转换为实际消息?

我已将缓冲区大小设置为 1,这样我就可以看到当所有发送的数据都不适合缓冲区时会发生什么。这是我在 Client.cs:

中调用 stream.BeginRead() 的方法
// Deliberately setting the buffer size to 1, to simulate what happens when the message doesn't fit in the buffer.
int bufferSize = 1;
byte[] receiveBuffer;

private void ConnectCallback(IAsyncResult result)
{
    client.EndConnect(result);

    Console.WriteLine("Connected to server.");

    stream = client.GetStream();

    // At this point, the client is connected, and we're expecting a message: "Welcome!"
    receiveBuffer = new byte[bufferSize];
    stream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, new AsyncCallback(ReadCallback), stream);
}

private void ReadCallback(IAsyncResult result)
{
    int bytesLength = stream.EndRead(result);

    // Should be "Welcome!". But of course it's "W", because the bufferSize is 1.
    string message = Encoding.UTF8.GetString(receiveBuffer, 0, bytesLength);

    Console.WriteLine("Received message: {0}", receivedMessage);

    // Reset the buffer and begin reading a new message, which will be "e".
    // However, I want the whole message ("Welcome!") in one byte array.
    receiveBuffer = new byte[bufferSize];
    stream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReadCallback, null);
}

这是发送消息时的输出"Welcome!":

Connected to server.
Received message: W
Received message: e
Received message: l
Received message: c
Received message: o
Received message: m
Received message: e
Received message: !

我是否应该在整个消息到达之前临时存储数据,然后将其转换为字符串?

后续问题:如果 2 条消息紧接着发送,例如 Welcome! 然后 What's your name 怎么办?那如何区分​​这两个消息呢?

Should I temporary store the data until the whole message has arrived, and then convert that to a string?

是的,完全正确。

Follow up question: What if 2 messages are sent closely after each other, for example Welcome! and then What's your name? How do I distinguish the two messages then?

一般方法是在发送消息本身之前发送消息的长度。这样接收方就知道什么时候收到了完整的包裹。

正如 500 - 内部服务器错误已经指出的那样,您为此使用了缓冲区。这是一些代码示例: 接收:

while (true) //you should use a bool variable here to stop this on disconnect.
                {
                    byte[] bytes;

                    bytes = ReadNBytes(ns, 4);
                    //read out the length field we know is there, because the server always sends it.
                    int msgLenth = BitConverter.ToInt32(bytes, 0);
                    bytes = ReadNBytes(ns, msgLenth);
                    //working with the buffer...
                    if (bytes.Length > 0)
                    {
                        try
                        {
                            //do stuff here. bytes contains your complete message.
                        }
                        catch (Exception e) { Log(e.Message); }                            
                    }
                }


public static byte[] ReadNBytes(NetworkStream stream, int n)
    {
        byte[] buffer = new byte[n];
        try
        {
            int bytesRead = 0;

            int chunk;
            while (bytesRead < n)
            {
                chunk = stream.Read(buffer, (int)bytesRead, buffer.Length - (int)bytesRead);
                if (chunk == 0)
                {
                    // error out
                    Log("Unexpected disconnect");
                    stream.Close();
                }
                bytesRead += chunk;
            }
        }
        catch (Exception e) { Log(e.Message); }

        return buffer;
    }

要发送内容,请使用以下链接:

public static void SendObject(NetworkStream ns, byte[] data)
    {
        byte[] lengthBuffer = BitConverter.GetBytes(data.Length);
        ns.Write(lengthBuffer, 0, lengthBuffer.Length);
        ns.Write(data, 0, data.Length);
    }

希望对您有所帮助!