封装stream,在读写stream时提供事件通知

前阵子的工作涉及一些网络编程,使用了面向流的方式做传输数据。在代码过程中,遇到一个新需求就是要统计流量。其实最简单的办法就时在读写流的地方增加代码,把功能增加上去就可以。但是我觉得那样对我原理的代码框架影响较大,基于尽量不影响原来的代码的考虑,我想到了Decorator设计模式。

先把代码贴出来,在做解释吧:

   

以下为引用的内容:
 public class EventStream : Stream
    {
        public event EventHandler<FStreamDataEventArgs> OnBeforeRead;
        public event EventHandler<FStreamDataEventArgs> OnBeforeWrite;
        private Stream stream;
        public EventStream(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException("EventStream");
            this.stream = stream;
        }

        [ ==== Stream members ==== ]#region [ ==== Stream members ==== ]
        public override bool CanRead
        {
            get { return stream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return stream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return stream.CanWrite; }
        }

        public override void Flush()
        {
            stream.Flush();
        }

        public override long Length
        {
            get { return stream.Length; }
        }

        public override long Position
        {
            get
            {
                return stream.Position;
            }
            set
            {
                stream.Position = value;
            }
        }

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

        public override long Seek(long offset, SeekOrigin origin)
        {
            return stream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            stream.SetLength(value);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (OnBeforeWrite != null)
                OnBeforeWrite(this, new 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)
            {
                this.internalHandler = internalHandler;
                this.callback = 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
        {
            object state;
            FStreamDataEventArgs streamDataEventArgs;

            public object State
            {
                get { return state; }
            }

            public FStreamDataEventArgs StreamDataEventArgs
            {
                get { return streamDataEventArgs; }
            }

            public InternalAsyncState(FStreamDataEventArgs streamDataEventArgs, object state)
            {
                this.streamDataEventArgs = streamDataEventArgs;
                this.state = state;
            }
        }

        private class EventStreamAsyncResult : IAsyncResult
        {
            IAsyncResult ar;

            public EventStreamAsyncResult(IAsyncResult ar)
            {
                if (ar == null) throw new ArgumentNullException("EventStreamAsyncResult");
                this.ar = ar;
            }
            IAsyncResult Members#region IAsyncResult Members

            public object AsyncState
            {
                get
                {
                    InternalAsyncState myState = ar.AsyncState as InternalAsyncState;
                    if (myState != null)
                        return myState.State;
                    else
                        return ar.AsyncState;
                }
            }

            internal IAsyncResult InternalAsyncResult
            {
                get { return ar; }
            }

            public System.Threading.WaitHandle AsyncWaitHandle
            {
                get { return ar.AsyncWaitHandle; }
            }

            public bool CompletedSynchronously
            {
                get { return ar.CompletedSynchronously; }
            }

            public bool IsCompleted
            {
                get { return ar.IsCompleted; }
            }

            #endregion
        }
    }

    public class FStreamDataEventArgs : EventArgs
    {
        private byte[] buffer;
        private int offset;
        private int count;

        public FStreamDataEventArgs(byte[] buffer, int offset, int count)
        {
            if(buffer == null) throw new ArgumentNullException("FStreamDataEventArgs");
            if(offset + count>buffer.Length) throw new ArgumentOutOfRangeException("FStreamDataEventArgs");

            this.buffer = buffer;
            this.offset = offset;
            this.count = count;
        }

        /**//// <summary>
        /// 数据缓存
        /// </summary>
        public byte[] Buffer
        {
          get { return buffer; }
        }

        /**//// <summary>
        /// 数据开始位置
        /// </summary>
        public int Offset
        {
          get { return offset; }
        }

        /**//// <summary>
        /// 数据长度
        /// </summary>
        public int Count
        {
          get { return count; }
        }
    }

刚开始以为很简单,事实上写下来还挺多行代码的,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中的类都给包装起来,并在完成原来的功能之余另外加了自己的功能。

文笔一般,希望能对你有帮助。

时间: 2024-11-09 00:18:53

封装stream,在读写stream时提供事件通知的相关文章

easyui-用户登陆按Enter提交时,keydown事件覆盖了$.messager.alert()弹出框

问题描述 用户登陆按Enter提交时,keydown事件覆盖了$.messager.alert()弹出框 在用户登陆界面中,在页面初始化时,添加键盘事件: $(function() { $(document).keydown(function(e){ if(e.keyCode == 13){ login(); } }); }); function login(){ $.ajax();发送请求在回调函数中调用easyui提供的 $.messager.alert('登录提示', '用户名不存在!',

js实现浏览器窗口大小被改变时触发事件的方法_javascript技巧

本文实例讲述了js实现浏览器窗口大小被改变时触发事件的方法.分享给大家供大家参考.具体分析如下: 当浏览器的窗口大小被改变时触发的事件window.onresize 为事件指定代码: 复制代码 代码如下: window.onresize = function(){ } 例如: 浏览器可见区域信息: 复制代码 代码如下: <span id="info_jb51_net">请改变浏览器窗口大小</span> <script> window.onresiz

winforms-VB.net下如何实现鼠标进入窗体某区域时触发事件

问题描述 VB.net下如何实现鼠标进入窗体某区域时触发事件 VB.net Winforms开发form上有一个mediaplayer控件,在上面设置一个隐藏的(visible=false)按钮如何实现鼠标进入按钮附近区域时触发button1.visible=true的事件 因为按钮是在mediaplayer之上的,所以之前尝试的画Rectangle来检测鼠标位置的方法不行 想来想去只有通过PointToClient的方法实现,但是不知道具体怎么操作 求教,谢谢

xcode-点击自定义按钮时触发事件

问题描述 点击自定义按钮时触发事件 创建了一个TableView,然后创建了自定义的单元,其中放了一个Button.现在我想要实现,点击索引的时候获取tableview的索引内容,但是点击了对应索引的按钮之后,没有给出索引列表. 我的tableview类名是SubMenuViewController,然后单元类名是SubMenuCell, SubMenuViewController代码如下: - (UITableViewCell *)tableView:(UITableView *)tableV

gtk+程序在关闭主窗口时的事件流

当鼠标单击gtk+窗口的关闭按钮时,程序首先接收到delete_event,当该事件处理函数返回TRUE表示事件已处理禁止进一步传播,从而取消关闭操作:当返回FALSE时,事件消息进一步向上传播,此时会调用gtk+默认的处理函数,该函数产生destroy事件,一般在该事件的处理函数中用gtk_main_quit将窗口关闭:这时gtk_main()消息环退出,因而程序退出. 下面是示例代码: #include <gnome.h> gboolean delete_handle(GtkWidget

flex 监听事件-flex中的datefield组件中点击下拉选择日期改变时派发事件

问题描述 flex中的datefield组件中点击下拉选择日期改变时派发事件 如题,我需要在选择日期时监听change事件,执行脚本,但是为什么每次会执行两次脚本. 怎么可以控制只在选择日期之后派发,而在点击时不派发. /** 添加监听事件 / this.addEventListener(CalendarLayoutChangeEvent.CHANGE, selectedChange); /* 对象更改处理函数 */ protected function selectedChange(event

点击label时click事件被触发两次的坑

原文:点击label时click事件被触发两次的坑 今天帮群里的朋友看一段代码的时候偶然间遇到一个label的坑,点击label的时候,监听的click事件被执行两次: 具体代码如下: 1 <div id="test"> 2 <input type="checkbox" name="abc" id="abc"/> 3 <label for="abc">3423432432

easyui Enter提交时,keydown事件覆盖了$.messager.alert()弹出框

问题描述 easyui Enter提交时,keydown事件覆盖了$.messager.alert()弹出框 easyui Enter键提交查询条件,如果日期不符合条件,弹出提示框,但是弹出框弹不出来 ,应该怎么解决,代码如下: /*查询方法 */ function queryData(){ var beginDate = $("#createStartDate").datebox("getValue"); var endDate = $("#create

java fatjar-eclipse生成的fatjar在别的电脑上运行时按钮事件无法响应

问题描述 eclipse生成的fatjar在别的电脑上运行时按钮事件无法响应 在自己电脑上运行正常,在别的电脑上只能弹出最初的界面,点击界面上的按钮无法弹出相应的面板,求大神们帮忙看看