PeanutLee

TO BE PROFESSIONAL

导航

实战Decorator模式:封装stream,在读写stream时提供事件通知

Posted on 2007-04-29 01:22  Peanut  阅读(2599)  评论(2)    收藏  举报
    前阵子的工作涉及一些网络编程,使用了面向流的方式做传输数据。在代码过程中,遇到一个新需求就是要统计流量。其实最简单的办法就时在读写流的地方增加代码,把功能增加上去就可以。但是我觉得那样对我原理的代码框架影响较大,基于尽量不影响原来的代码的考虑,我想到了Decorator设计模式。
    先把代码贴出来,在做解释吧:
    public class EventStream : Stream
    
{
        
public event EventHandler<FStreamDataEventArgs> OnBeforeRead;
        
public event EventHandler<FStreamDataEventArgs> OnBeforeWrite;

        
private Stream stream;
        
public EventStream(Stream stream)
        
{
            
if (stream == nullthrow new ArgumentNullException("EventStream");
            
this.stream = stream;
        }


        
#region [ ==== Stream members ==== ]
        
public override bool CanRead
        


        
public override bool CanSeek
        


        
public override bool CanWrite
        


        
public override void Flush()
        


        
public override long Length
        


        
public override long Position
        


        
public override int Read(byte[] buffer, int offset, int count)
        
{
            
int readSize = stream.Read(buffer, offset, count);
            
if (OnBeforeRead != null)
                OnBeforeRead(
thisnew FStreamDataEventArgs(buffer, offset, readSize));
            
return readSize;
        }


        
public override long Seek(long offset, SeekOrigin origin)
        


        
public override void SetLength(long value)
        


        
public override void Write(byte[] buffer, int offset, int count)
        
{
            
if (OnBeforeWrite != null)
                OnBeforeWrite(
thisnew FStreamDataEventArgs(buffer, offset, count));
            stream.Write(buffer, offset, count);
        }


        
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, 
            AsyncCallback callback, 
object state)
        
{
            InternalAsyncState myState 
= new InternalAsyncState(
                              new FStreamDataEventArgs(buffer, offset, count), state);
            AsyncCallback myCallback 
= new AsyncCallback(
                              new InternalCallback(OnBeforeRead, callback).Callback);
            
return new EventStreamAsyncResult(
                                 stream.BeginRead(buffer, offset, count, myCallback, myState));
        }


        
public override int EndRead(IAsyncResult asyncResult)
        
{
            EventStreamAsyncResult esar 
= asyncResult as EventStreamAsyncResult;
            
if (esar != null)
                
return stream.EndRead(esar.InternalAsyncResult);
            
else
                
return stream.EndRead(asyncResult);
        }


        
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        
{
            InternalAsyncState myState 
= new InternalAsyncState(
                              new FStreamDataEventArgs(buffer, offset, count), state);
            AsyncCallback myCallback 
= new AsyncCallback(
                              new InternalCallback(OnBeforeWrite, callback).Callback);
            
return new EventStreamAsyncResult(
                              stream.BeginWrite(buffer, offset, count, myCallback, myState));
        }


        
public override void EndWrite(IAsyncResult asyncResult)
        
{
            stream.EndWrite(asyncResult);
        }


        
#endregion


        
private class InternalCallback
        
{
            
private AsyncCallback callback;
            
private EventHandler<FStreamDataEventArgs> internalHandler;

            
public InternalCallback(EventHandler<FStreamDataEventArgs> internalHandler, AsyncCallback callback)
            


            
internal void Callback(IAsyncResult asyncResult)
            
{
                InternalAsyncState myState 
= asyncResult.AsyncState as InternalAsyncState;
                
if (internalHandler != null && myState != null)
                    internalHandler(
this, myState.StreamDataEventArgs);
                callback(
new EventStreamAsyncResult(asyncResult));
            }

        }


        
private class InternalAsyncState
        


        
private class EventStreamAsyncResult : IAsyncResult
        

    }


    
public class FStreamDataEventArgs : EventArgs
    

刚开始以为很简单,事实上写下来还挺多行代码的,^_^。
Decorator模式嘛,当然先继承stream,把stream本来该做的事情先完成了。这个很简单类里面包含一个内部的stream,stream该有的接口都由它来完成了。接下来就是增加两个事件,分别是OnBeforeRead、OnBeforeWrite。名字里面都有Before,其实我考虑到数据流都会通过这两个事件开放出来,你想做加密什么的都可以,当然也包括我想要的统计数据流量。
接下来就是在读写流的时候触发这两个事件就可以了。看看同步的Read、Write方法,简单的调用就可以了。
关键的地方就在于异步的读写。
我们先看看一般Stream的异步调用代码是怎么样的:

stream.BeginRead(buffer, 0, byte2read, new AsyncCallback(EndReadCallback), state);

private void EndReadCallback(IAsyncResult asyncResult)
{
    
object state = asyncResult.AsyncState;
    nReadSize 
= stream.EndRead(asyncResult);
            //
}

在不更改这个“client”代码的情况下,要怎么样在stream那边知道这里的确实读了多少数据呢?
 显然在调用BeginRead的时候是不知道,那就只能对这个AsyncCallback做手脚了。可以预想到framework内部会在完成了Read的操作之后会调用AsyncCallback委托来通知结果。于是我就传一个我定义好的AsyncCallback委托给BeginRead。当然还要把“client”提供的AsyncCallback给包装起来,在做完我的事情(事件通知)之后,还是要把“client”要我办的事情给也给办了(调用"client"的AsyncCallback委托来通知结果)。
这就在实现了“在客户代码与framework之间插一脚”。
再来看看我是怎么做到事件通知的。首先要把我要的数据给传过去,于是有了InternalAsyncState,这里面要有我触发事件需要的事件参数,还应该要包括用户可能传入的state。具体大家看看InternalAsyncState的实现。
最后多考虑了一点就是,假如“client”代码不是像我写的那样,而是不断的通过检查Stream.BeginRead 方法返回的IAsyncResult的IsCompleted属性来确定是否Read完成的话,那我的代码就有问题了,我返回的IAsyncResult根本就不是原理的IAsyncResult了。EventStreamAsyncResult类就是为这个而写的。
下面是使用的代码:

public void GetResponseStream()
{
        EventStream es 
= new EventStream(tcpClient.NetStream);
        es.OnBeforeRead 
+= new EventHandler<FStreamDataEventArgs>(EventStream_OnBeforeRead);
        es.OnBeforeWrite 
+= new EventHandler<FStreamDataEventArgs>(EventStream_OnBeforeWrite);
        
return es;
}

回头看看代码,其实都在用Decorator模式的思想,把原来的framework中的类都给包装起来,并在完成原来的功能之余另外加了自己的功能.。
文笔一般,希望能对你有帮助。