我使用Socket
通过TCP接收数据,TextReader.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>
我不希望“这是一个te”返回。相反,我希望“这是一个测试”,等待直到收到整行。
代码:
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);
}
感谢。
答案 0 :(得分:3)
听起来这些数据是通过一些额外的分隔符发送的。假设您在网络流上使用StreamReader,它应该完全按照您的预期运行。我建议您使用Wireshark查看套接字正在接收的确切数据。
我也怀疑它返回null然后是另一行 - 你确定你不是说它返回一个空字符串然后是另一行吗?
编辑:现在您已经发布了代码,原因更加清晰 - 您一次只能解码一个缓冲区。这真的不行,可能会以更严肃的方式打破。缓冲区甚至可能不会在字符边界处中断。老实说,同步阅读并使用StreamReader
会更容易。异步执行此操作时,您应该使用System.Text.Decoder
,如果需要,可以存储任何先前的状态(从前一个缓冲区的末尾)。你还必须存储以前的大部分内容都被阅读 - 我怀疑你根本无法使用TextReader
,或者至少你必须对这种情况进行特殊处理最后一个字符是'\ r'或'\ n'。请记住,一个缓冲区可以以'\ r'结尾,下一个缓冲区以'\ n'开头,表示它们之间有一个换行符。看看它有多难?
你肯定需要异步处理吗?
编辑:听起来你可以做一些基本上可以将数据转储到的东西,并附加一个“LineCompleted”事件处理程序。您可以将事件处理程序附加到开始,然后只是将数据转储到其中,直到没有更多数据(此时您需要告诉它数据已完成)。如果这听起来合适,我可能会尝试为MiscUtil开设这样的课程 - 但是我不太可能在下周完成它(我现在真的很忙)。答案 1 :(得分:0)
有一个缓冲区(开始为空),每次阅读
请注意,这可以达到你想要的效果,但是,使用任何此类方案,您现在必须担心如何处理缓冲区太长的行。
- MarkusQ
答案 2 :(得分:0)
查看我对previous very similar question的回答。它涉及异步套接字I / O和以类似流的方式读取行。希望有所帮助。
答案 3 :(得分:0)
这里可以看到几个问题:
单个Unicode代码点可以跨数据包进行拆分,因此您需要保留自己的Utf8Encoding实例。或者将完整的消息缓冲为byte [],并在知道它完成时一次转换。
您需要一种确定何时收到完整邮件的方法。您需要继续阅读直到完成(并处理您在同一个Read呼叫中开始接收下一个数据包的情况。
答案 4 :(得分:0)
我决定编写自己的ReadLine解析器 - 有点像。这是代码:
// 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);
}
非常感谢@Jon Skeet,@ Novaorin和@Richard提出的非常有用的建议。你的共同努力使我得到了最终解决方案。 =]