【问题标题】:Asynchronous socket, receive string messages异步套接字,接收字符串消息
【发布时间】:2011-07-21 10:52:16
【问题描述】:

晚上好,提前写了这么多,但我不知道错误在哪里......

我的客户端应用程序正在从服务器异步接收。我想一次传输一堆东西(数组的内容,几百字节)。

我希望服务器能够发送“命令”,并在客户端有一个函数根据这些命令进行操作,例如,如果来自服务器的消息读取"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() 之后正确递增和打印。然而,字符串仍然没有得到更新。

【问题讨论】:

  • 你在哪里声明了 mainBuffer?您确定不是每次收到时都将其重置为“”吗?

标签: c# multithreading string sockets console


【解决方案1】:

这是你的问题“乱七八糟的代码行!”

//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 索引的字节数组时,它将使用默认值byte0 填充数组的所有索引。但0 只是等于终端字符\0

byte b = (byte)`\0`;\\the value of b will be 0

因此,当您调用Read(buffer) 时,该方法不会修剪 缓冲区以仅适合 读取的数据。所以如果缓冲区大小“here 1024”大于读取的数据,缓冲区的所有剩余字节将等于终端字符'\0',因此生成的字符串的字符数组将为ABC\0\0\0\0... to the index 1024。当您向其中添加字符串DEF 时,它会将其添加到char 数组“在最后一个\0 之后”的最后一个索引处,那么char 数组将是ABC\0\0\0\0...DEF,但由于DEF在终端字符之后添加,因此Console.Write 将忽略第一个\0! 之后的所有字符。

在调试时还要注意,如果您将鼠标指向mainBuffer 变量,您将看到它包含的实际数据可能类似于ABC\0\0\0\0..DEF\0\0\0\0..GHI

但是要解决问题并仅生成可靠的字符串,请获取读取的实际字节并仅从中生成字符串。所以:

int dataRead = 0;

while ((dataRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
    List<byte> actualBuffer = (new List<byte>(buffer)).GetRange(0, dataRead);

    string data = System.Text.Encoding.UTF8.GetString(actualBuffer.ToArray());
    Console.WriteLine("Raw data: {0}", data));
    mainBuffer += data;
    Console.WriteLine(mainBuffer);
}

这里很生气,您应该考虑使用StringBuilder 而不是string

【讨论】:

    最近更新 更多