如果它不适合 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);
}
希望对您有所帮助!
我最近开始在 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);
}
希望对您有所帮助!