从C#中的套接字中检索整行?

时间:2016-05-14 05:34:02

标签: c# python sockets

我有一个简单的客户端 - 服务器系统发送纯文本 - 尽管只有已经批准的命令。服务器是一个Python系统 - 我已经确认了正确的连接。

然而,客户端是C# - 在Unity中。在搜索示例时,我偶然发现了这段代码。它确实似乎做了我想要的,但只是部分:

public String readSocket()
{
    if (!socketReady)
        return "";
    if (theStream.DataAvailable)
        return theReader.ReadLine();
    return "";
}

我发送的字符串以\ n结束,但我只得到这样的消息的一半:

消息A:

  1. claim_2
  2. 消息B:

    1. _20_case

    2. claim_1

    3. 我知道这可能与我如何直接阅读该行有关,但我找不到更好的例子 - 奇怪的是,即使有多个人指出问题,每个人似乎都会回到这个片段。

      可以做些什么来正确修复这段代码吗?

      如果有帮助,我会发送信息(来自我的Python服务器):

      action = str(command) + "_" + str(x) + "_" + str(userid) + "_" + str(user)
      cfg.GameSendConnection.sendall((action + "\n").encode("utf-8"))
      

2 个答案:

答案 0 :(得分:0)

当你进行套接字编程时,重要的是要注意数据可能不是 一体式提供。事实上,这正是你所看到的。您的 消息正在被打破。

那么为什么ReadLine不要等到有一行要读?

以下是一些简单的示例代码:

var stream = new MemoryStream();
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream) { AutoFlush = true };
writer.Write("foo");
stream.Seek(0, SeekOrigin.Begin);
Console.WriteLine(reader.ReadLine());

请注意,最后没有换行符。不过,这个输出很少 代码段为foo

ReadLine将字符串返回到第一个换行符或直到没有 更多要读取的数据。从没有更多数据的流中读取异常 要读取的数据,然后返回null

NetworkStream的{​​{1}}属性返回true时,它有 数据。但如前所述,无法保证这一点 数据是。它可能是一个字节。或者是消息的一部分。或者完整的消息 加上下一条消息的一部分。请注意,根据编码,它可以 甚至可以只接收角色的一部分。不是所有角色 编码的所有字符最多只能是一个字节。这包括DataAvailable发送的UTF-8。

如何解决这个问题?读取字节,而不是行。把它们放在缓冲区里。每一次之后 读取,检查缓冲区是否包含换行符。如果是的话,你现在已经满了 要处理的消息。从中移除消息,包括换行符 缓冲并继续向其追加新数据,直到收到下一个换行符。和 等等。

答案 1 :(得分:0)

这就是我在类似应用程序中处理整行的方法,这是一个非常简单的代码,你的代码可能不同,但你可以理解。

private string incompleteRecord = "";
public void ReadSocket()
{
    if (_networkStream.DataAvailable)
    {
        var buffer = new byte[8192];
        var receivedString = new StringBuilder();
        do
        {
            int numberOfBytesRead = _networkStream.Read(buffer, 0, buffer.Length);
            receivedString.AppendFormat("{0}", Encoding.UTF8.GetString(buffer, 0, numberOfBytesRead));
        } while (_networkStream.DataAvailable);
        var bulkMsg = receivedString.ToString();

        // When you receive data from the socket, you can receive any number of messages at a time
        // with no guarantee that the last message you receive will be complete.
        // You can receive only part of a complete message, with next part coming
        // with the next call. So, we need to save any partial messages and add
        // them to the beginning of the data next time.
        bulkMsg = incompleteRecord + bulkMsg;
        // clear incomplete record so it doesn't get processed next time too.
        incompleteRecord = "";
        // loop though the data breaking it apart into lines by delimiter ("\n")
        while (bulkMsg.Length > 0)
        {
            var newLinePos = bulkMsg.IndexOf("\n");
            if (newLinePos > 0)
            {
                var line = bulkMsg.Substring(0, newLinePos);
                // Do whatever you want with your line here ...
                // ProcessYourLine(line)

                // Move to the next message.
                bulkMsg = bulkMsg.Substring(line.Length + 1);
            }
            else
            {
                // there are no more newline delimiters
                // so we save the rest of the message (if any) for processing with the next batch of received data.
                incompleteRecord = bulkMsg;
                bulkMsg = "";
            }
        }
    }
}