我有一个Stream对象偶尔可以获取一些数据,但是间隔时间不可预测。流上出现的消息是明确定义的,并提前声明其有效负载的大小(大小是每个消息的前两个字节中包含的16位整数)。
我想要一个StreamWatcher类来检测Stream何时有一些数据。一旦它发生,我想要引发一个事件,以便订阅的StreamProcessor实例可以处理新消息。
这可以在不使用Threads的情况下使用C#事件完成吗?看起来它应该是直截了当的,但我不能完全理解设计它的正确方法。
答案 0 :(得分:14)
如果你说不直接使用 ,我认为你仍然希望通过异步调用间接使用间接,否则这不会非常有用。
您需要做的就是包装Stream
的异步方法并将结果存储在缓冲区中。首先,让我们定义规范的事件部分:
public delegate void MessageAvailableEventHandler(object sender,
MessageAvailableEventArgs e);
public class MessageAvailableEventArgs : EventArgs
{
public MessageAvailableEventArgs(int messageSize) : base()
{
this.MessageSize = messageSize;
}
public int MessageSize { get; private set; }
}
现在,从流中异步读取一个16位整数,并在准备就绪时报告:
public class StreamWatcher
{
private readonly Stream stream;
private byte[] sizeBuffer = new byte[2];
public StreamWatcher(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
this.stream = stream;
WatchNext();
}
protected void OnMessageAvailable(MessageAvailableEventArgs e)
{
var handler = MessageAvailable;
if (handler != null)
handler(this, e);
}
protected void WatchNext()
{
stream.BeginRead(sizeBuffer, 0, 2, new AsyncCallback(ReadCallback),
null);
}
private void ReadCallback(IAsyncResult ar)
{
int bytesRead = stream.EndRead(ar);
if (bytesRead != 2)
throw new InvalidOperationException("Invalid message header.");
int messageSize = sizeBuffer[1] << 8 + sizeBuffer[0];
OnMessageAvailable(new MessageAvailableEventArgs(messageSize));
WatchNext();
}
public event MessageAvailableEventHandler MessageAvailable;
}
我认为就是这样。这假定处理消息的任何类也可以访问Stream
并准备基于事件中的消息大小同步或异步地读取它。如果您希望观察者类实际读取整个消息,那么您将需要添加更多代码来执行此操作。
答案 1 :(得分:2)
是的,这可以做到。将非阻止Stream.BeginRead方法与AsyncCallback一起使用。当数据可用时,将异步调用回调。在回调中,调用Stream.EndRead来获取数据,然后再次调用Stream.BeginRead以获取下一个数据块。缓冲传入数据的字节数组,该数组足以容纳消息。一旦字节数组已满(可能需要多次回调调用),请引发事件。然后读取下一个消息大小,创建一个新缓冲区,重复,完成。
答案 2 :(得分:1)
正常方法是使用Stream
公开的.NET asynchronous programming pattern。本质上,您通过调用Stream.BeginRead
异步开始读取,向其传递byte[]
缓冲区和回调方法,该方法将在从流中读取数据时调用。在回调方法中,您调用Stream.EndRead
,向其传递给回调的IAsncResult
参数。 EndRead
的返回值告诉您读入缓冲区的字节数。
以这种方式收到前几个字节后,您可以再次调用BeginRead
等待剩余的消息(如果第一次没有获得足够的数据)。一旦你得到了整个信息,你就可以举起活动。
答案 3 :(得分:1)
是否使用Stream.BeginRead()与在单独的线程中使用同步Stream.Read()方法相同?