我正在编写.NET XMPP库以获得乐趣,并且已经讨论过elsewhere .NET 4.5之前版本中的XmlReader
实现不适合从{{1}解析XML因为在填充内部4KB缓冲区或达到EOF之前它不会开始解析。
其他图书馆完全没有使用NetworkStream
。如前面链接的问题所述,jabber-net使用Java XML解析器的端口。我在搜索时发现的实现,Babel IM,使用自己的simple XML parser。我不确定agsXMPP是做什么的。
但是,随着.NET 4.5的发布,新的异步功能XmlReader
显然已经升级,现在可以实现async parsing。因此我用它来破解一个相当简单的XMPP客户端,它可以连接到服务器并发送和接收消息。
然而,实际情况似乎是从服务器断开连接。在断开连接时,我通常只想要XmlReader
实例的Dispose()
和基础流。但是,XmlReader
实际上会抛出Dispose()
,并显示消息“异步操作已在进行中”。如果你在异步时调用它......那么消息说的是什么。但是,由于XMPP的性质,我的InvalidOperationException
基本上不断地执行异步操作,因为它等待来自服务器的XML节来到管道。
XmlReader
上似乎没有任何methods我可以用来告诉它取消任何待处理的异步操作,以便我可以XmlReader
干净利落。 是否有更好的方法来处理这种情况,而不是仅仅试图处理Dispose()
? XMPP spec表明服务器应该发送关闭{{ 1}}标签断开连接。我可以使用它作为一个信号,不要尝试执行另一个异步读取,因为没有其他任何东西应该从管道下来,但是不能保证这一点。
以下是一些示例代码。 XmlReader
基本上模拟一个开放的</stream:stream>
,因为它永远不会到达EOF并且会阻塞,直到至少可以读取1个字节。你可以“注入”XML文本,LongLivedTextStream
很乐意解析它,但是试图处理读者会触发上述异常。
NetworkStream
修改
此示例使用实际的XmlReader
,并显示如果基础流的using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace Example
{
class LongLivedTextStream : Stream
{
ManualResetEvent moarDatas = new ManualResetEvent(false);
List<byte> data = new List<byte>();
int pos = 0;
public void Inject(string text)
{
data.AddRange(new UTF8Encoding(false).GetBytes(text));
moarDatas.Set();
}
public override int Read(byte[] buffer, int offset, int count)
{
var bytes = GetBytes(count).ToArray();
for (int i = 0; offset + i < buffer.Length && i < bytes.Length; i++)
{
buffer[offset + i] = bytes[i];
}
return bytes.Length;
}
private IEnumerable<byte> GetBytes(int count)
{
int returned = 0;
while (returned == 0)
{
if (pos < data.Count)
{
while (pos < data.Count && returned < count)
{
yield return data[pos];
pos += 1; returned += 1;
}
}
else
{
moarDatas.Reset();
moarDatas.WaitOne();
}
}
}
#region Other Stream Members
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush() { }
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
#endregion
}
public class Program
{
public static void Main(string[] args)
{
Test();
Console.ReadLine();
}
public static async void Test()
{
var stream = new LongLivedTextStream();
var reader = XmlReader.Create(stream, new XmlReaderSettings() { Async = true });
var t = Task.Run(() =>
{
stream.Inject("<root>");
Thread.Sleep(2000);
stream.Inject("value");
Thread.Sleep(2000);
stream.Inject("</root>");
Thread.Sleep(2000);
reader.Dispose(); // InvalidOperationException: "An asynchronous operation is already in progress."
Console.WriteLine("Disposed");
});
while (await reader.ReadAsync())
{
bool kill = false;
switch (reader.NodeType)
{
case XmlNodeType.Element:
Console.WriteLine("Start: " + reader.LocalName);
break;
case XmlNodeType.EndElement:
Console.WriteLine("End: " + reader.LocalName);
//kill = true; // I could use a particular EndElement as a signal to not try another read
break;
case XmlNodeType.Text:
Console.WriteLine("Text: " + await reader.GetValueAsync());
break;
}
if (kill) { break; }
}
}
}
}
或NetworkStream
,则Close()
上的Dispose()
调用不会返回false如希望的那样,它继续阻止。
ReadAsync()
答案 0 :(得分:1)
尝试将手册</stream:stream>
注入解析器。为此,您可能需要NetworkStream和解析器之间的适配器类,它将所有传入数据传递给解析器,但添加了另一种方法来注入</stream:stream>
。当你调用那个方法时,你可能需要注意不要处于另一个节的中间,可能是通过在解析器的输出端保持状态。