C# TextReader.ReadLine返回不完整的行

C# TextReader.ReadLine返回不完整的行,c#,networking,C#,Networking,我正在使用套接字通过TCP接收数据,并使用文本阅读器.ReadLine从连接中读取行。未收到完整行时出现问题--TextReader.ReadLine返回不完整的字符串。我希望它返回null,表示无法读取整行。我该怎么做 基本上,我收到了以下数据: "hello\nworld\nthis is a test\n" 当我运行ReadLine时,我会得到以下结果: "hello" "world" "this is a te" <null> <socket gets more da

我正在使用
套接字
通过TCP接收数据,并使用
文本阅读器.ReadLine
从连接中读取行。未收到完整行时出现问题--
TextReader.ReadLine
返回不完整的字符串。我希望它返回
null
,表示无法读取整行。我该怎么做

基本上,我收到了以下数据:

"hello\nworld\nthis is a test\n"
当我运行
ReadLine
时,我会得到以下结果:

"hello"
"world"
"this is a te"
<null>
<socket gets more data>
"st"
<null>

谢谢。

听起来数据是用一些额外的分隔符发送的。假设您在网络流上使用StreamReader,它的行为应该完全符合您的预期。我建议您使用Wireshark查看套接字接收到的确切数据

我还怀疑它是否返回null,然后返回另一行-您确定这不是指它返回空字符串,然后返回另一行吗

编辑:现在你已经发布了代码,原因更清楚了——你一次只解码一个缓冲区。这真的行不通,可能会以更严重的方式破坏。缓冲区甚至可能不会在字符边界处中断

老实说,同步阅读和使用
StreamReader
会容易得多。异步执行时,应该使用
System.Text.Decoder
,如果需要,它可以存储任何以前的状态(从上一个缓冲区的末尾开始)。您还必须存储前一行的读取量-我怀疑您根本无法使用
TextReader
,或者至少您必须对最后一个字符为'\r'或'\n'的情况进行特殊处理。请记住,一个缓冲区可以以“\r”结尾,下一个缓冲区可以以“\n”开头,表示它们之间的一个换行符。看看有多困难

你真的,真的需要异步处理吗


编辑:听起来您可以处理一些基本上可以将数据转储到其中并附加“LineCompleted”事件处理程序的事情。您可以先附加事件处理程序,然后继续将数据转储到事件处理程序中,直到没有更多数据为止(此时您需要告诉事件处理程序数据已经完成)。如果这听起来合适的话,我可能会尝试为MiscUtil开发这样一个类——但我不太可能在下周内完成它(我现在真的很忙)。

听起来数据是用一些额外的分隔符发送的。假设您在网络流上使用StreamReader,它的行为应该完全符合您的预期。我建议您使用Wireshark查看套接字接收到的确切数据

我还怀疑它是否返回null,然后返回另一行-您确定这不是指它返回空字符串,然后返回另一行吗

编辑:现在你已经发布了代码,原因更清楚了——你一次只解码一个缓冲区。这真的行不通,可能会以更严重的方式破坏。缓冲区甚至可能不会在字符边界处中断

老实说,同步阅读和使用
StreamReader
会容易得多。异步执行时,应该使用
System.Text.Decoder
,如果需要,它可以存储任何以前的状态(从上一个缓冲区的末尾开始)。您还必须存储前一行的读取量-我怀疑您根本无法使用
TextReader
,或者至少您必须对最后一个字符为'\r'或'\n'的情况进行特殊处理。请记住,一个缓冲区可以以“\r”结尾,下一个缓冲区可以以“\n”开头,表示它们之间的一个换行符。看看有多困难

你真的,真的需要异步处理吗

编辑:听起来您可以处理一些基本上可以将数据转储到其中并附加“LineCompleted”事件处理程序的事情。您可以先附加事件处理程序,然后继续将数据转储到事件处理程序中,直到没有更多数据为止(此时您需要告诉事件处理程序数据已经完成)。如果这听起来合适的话,我可能会尝试为MiscUtil编写这样一个类,但我不太可能在下周内完成它(我现在真的很忙)。

有一个缓冲区(开始为空),每次阅读时

  • 如果缓冲区中有\n,请删除所有内容 所有的东西,包括它和返回它
  • 尽可能地读取,并将读取的内容附加到缓冲区中
  • 如果由于eof导致读取失败,则返回并清除内容,除非缓冲区为空,在这种情况下,释放eof
  • 如果您阅读的内容中有\n错误,请从顶部重试, 否则返回空值
请注意,这将实现您想要的功能,但是,对于任何这样的方案,您现在都必须担心如何处理缓冲区中过长的行

--MarkusQ有一个缓冲区(开始时为空),每次读取时

  • 如果缓冲区中有\n,请删除所有内容 所有的东西,包括它和返回它
  • 尽可能地读取,并将读取的内容附加到缓冲区中
  • 如果由于eof导致读取失败,则返回并清除内容,除非缓冲区为空,在这种情况下,释放eof
  • 如果您阅读的内容中有\n错误,请从顶部重试, 否则返回空值
请注意,这将实现您想要的功能,但是,对于任何这样的方案,您现在都必须担心如何处理缓冲区中过长的行


--MarkusQ

查看我对一个问题的答案。它以类似流的方式涉及异步套接字I/O和读取行。希望这能有所帮助。

看看我对一个问题的答案。它以类似流的方式涉及异步套接字I/O和读取行。希望能有所帮助。

这里可以看到几个问题:

  • 单个Unicode代码点可以在数据包之间拆分,因此您需要保留自己的Utf8Encoding实例。改变
    var endPoint = ...;
    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    socket.Connect(endPoint);
    
    var stream = new NetworkStream(socket, true);
    
    var messageBuffer = new StringBuilder();
    
    // Data received async callback (called several times).
    int bytesRead = stream.EndRead(result);
    string data = Encoding.UTF8.GetString(readBuffer.Take(bytesRead).ToArray());
    messageBuffer.Append(data);
    
    using(var reader = new StringReader(messageBuffer.ToString()))
    {
        // This loop does not know that Message.Read reads lines.  For all it knows, it could read bytes or words or the whole stream.
    
        while((Message msg = Message.Read(reader)) != null)  // See below.
        {
            Console.WriteLine(msg.ToString());    // See example input/echo above.
        }
    
        messageBuffer = new StringBuilder(reader.ReadToEnd());
    }
    
    // Method of Message.
    public static Message Read(TextReader reader)
    {
        string line = reader.ReadLine();
    
        if(line == null)
            return null;
    
        return Message.FromRawString(line);
    }
    
    // Async callback.
    Message message;
    
    while((message = Message.ReadBytes(messageBuffer)) != null)
    {
        OnMessageReceived(new MessageEventArgs(message));
    }
    
    // Message class.
    public static Message ReadBytes(List<byte> data)
    {
        int end = data.FindIndex(b => b == '\n' || b == '\r');
    
        if(end == -1)
            return null;
    
        string line = Encoding.UTF8.GetString(data.Take(end).ToArray());
    
        data.RemoveRange(0, end + 1);
    
        if(line == "")
            return ReadBytes(data);
    
        if(line == null)
            return null;
    
        return Message.FromRawString(line);
    }