晚上好,提前抱歉写了这么多,但我不知道错误在哪里......
我的客户端应用程序是异步从服务器接收的。我想一次传输一堆东西(数组的内容,几百个字节)。
我希望服务器能够发送“命令”,并且在客户端有一个功能根据这些命令行动,例如,如果来自服务器的消息显示"print_hello"
,它应该调用一个函数打印你好。
现在,我的理解是,当异步接收数据时,我不知道已经发送了多少数据(或者是否发送了超出我预期的数据),所以我需要将所有数据存储在缓冲区,当收到“结束命令”(例如,'!'
)符号时,它应该知道调用该函数。
到目前为止,这对我来说很有意义,但我在实施它时遇到了麻烦。在我的DataReceived回调函数中,我有这段代码:
Console.WriteLine("Raw data: {0}", data));
mainBuffer += data;
Console.WriteLine(mainBuffer);
mainBuffer声明为volatile static string mainBuffer = "";
第一行正确打印,并按预期方式遍历所有数据。但是,当我打印出mainBuffer
时,它只会打印出我收到的第一组数据,其余的数据不会被添加到缓冲区中。
是什么导致这个?线程安全问题?我没有读过mainBuffer的最新值吗?我不能使用断点来调试它。
示例输出:
Raw data: ABC
ABC
Raw data: DEF
ABC
RAW data: GHI
ABC
小更新,我也尝试使用volatile static int
,并在每个DataReceived()
后递增并正确打印。但是字符串仍然没有更新。
答案 0 :(得分:3)
这是你的问题“乱码代码行!”:
//of course the problem has noting to do with the string being volatile...
private volatile string mainBuffer = string.Empty;
byte[] buffer = new byte[1024];
while (networkStream.Read(buffer, 0, buffer.Length) > 0)
{
string data = System.Text.Encoding.UTF8.GetString(buffer);
Console.WriteLine("Raw data: {0}", data));
mainBuffer += data;
Console.WriteLine(mainBuffer);
}
自然地,此代码的输出将如您之前提到的那样。以下是发生的事情:
C#中的string
类是一个char
数组,由指向数组中第一个char
的指针开始,以特殊的“终端”字符\0
结束。
当您创建n
索引的字节数组时,它将使用byte
的默认值0
填充数组的所有索引。但是0
等于终端字符\0
byte b = (byte)`\0`;\\the value of b will be 0
因此,当您调用Read(buffer)
时,该方法不会修剪缓冲区,只是适合读取的数据。因此,如果缓冲区大小“此处为1024”大于读取的数据,则缓冲区的所有剩余字节将等于终端字符'\ 0',因此生成的字符串的字符数组将为{{1} }。当您向其添加字符串ABC\0\0\0\0... to the index 1024
时,它会将DEF
数组的最后一个索引添加到最后char
之后“,\0
数组将为{{ 1}},但由于char
被添加到终端字符之后,因此ABC\0\0\0\0...DEF
将忽略所有DEF
之后的所有内容。
另外请注意,在调试时,如果将鼠标指向Console.Write
变量,您会看到它包含的实际数据可能是\0
但是要解决问题并仅生成可靠的字符串,请获取读取的实际字节数并仅从中生成字符串。所以:
mainBuffer
在此提及您应该考虑使用StringBuilder代替ABC\0\0\0\0..DEF\0\0\0\0..GHI
。