首页 新闻 会员 周边

异步Socket订阅接受数据事件丢失

0
悬赏园豆:5 [待解决问题]

一共有两个类:TcpClient和Session

Session类负责数据的接受并向外发布事件,主要代码如下:

/// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="cliSock">会话使用的Socket连接</param> 
        public Session(Socket cliSock)
        {
            Debug.Assert(cliSock != null);
            _cliSock = cliSock;
            _id = new SessionId((int)cliSock.Handle);
            byteRecvBuffer = new byte[DefaultBufferSize];
            IsConnected = true;
            thrdRecvData = new Thread(NotifyData);
            thrdRecvData.IsBackground = true;
            thrdRecvData.Start();
            //开始接受来自该客户端的数据 
            
        }

        public Session(Socket cliSock, DatagramResolver resolver)
            : this(cliSock)
        {
            this._resolver = resolver;
        }

 private void ReceiveData(IAsyncResult iar)
        {
            Socket client = (Socket)iar.AsyncState;
            try
            {
                //如果两次开始了异步的接收,所以当客户端退出的时候 
                //会两次执行EndReceive
                int recv = client.EndReceive(iar);

                if (recv == 0)
                {
                    //正常的关闭 
                    this.TypeOfExit = ExitType.NormalExit;
                    IsConnected = false;
                    if (SessionShutDown != null)
                    {
                        SessionShutDown(this, new NetEventArgs(this));
                    }
                    return;
                }

                for (int i = 0; i < recv; i++)
                {
                    listRecvByte.Add(byteRecvBuffer[i]);
                }
                //继续接收来自来客户端的数据 
                client.BeginReceive(byteRecvBuffer, 0, byteRecvBuffer.Length, SocketFlags.None,
                new AsyncCallback(ReceiveData), client);
            }
            catch (SocketException ex)
            {
                //客户端退出 
                if (10054 == ex.ErrorCode)
                {
                    //客户端强制关闭 
                    this.TypeOfExit = ExitType.ExceptionExit;
                    IsConnected = false;
                    if (SessionShutDown != null)
                    {
                        SessionShutDown(this, new NetEventArgs(this));
                    }
                    //CloseClient(client, Session.ExitType.ExceptionExit);
                }
            }
        }

        private void NotifyData()
        {
            _cliSock.BeginReceive(byteRecvBuffer, 0, byteRecvBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _cliSock);
            bool flag = true;
            List<byte> listBufferByte = new List<byte>();

            if (!IsConnected)
            {
                thrdRecvData = null;
            }

            while (IsConnected || listRecvByte.Count > 0)
            {
                Thread.Sleep(1);
                int dataCount = listRecvByte.Count;//获取当前已接受byte数据的数量存到变量中,以防在代码执行过程中接受到的byte总数量会更改
                if (dataCount > 0)
                {
                    //先去除缓存中的数据,一边本次处理后发布数据的存储
                    listBufferByte.Clear();
                    //遍历当前以获取的byte,查找分隔符所在索引
                    for (int i = 0; i < dataCount; i++)
                    {
                        flag = true;
                        listBufferByte.Add(listRecvByte[i]);//为若查找到分隔符之后,向外发布数据接受事件的数据参数做准备

                        if (listRecvByte[i] == _resolver.ByteEndTag[0])//找到了分隔符第一个byte的相等位置
                        {
                            //循环分隔符,继续判断是否对应
                            for (int j = 1; j < _resolver.ByteEndTag.Length; j++)
                            {
                                //若已超出当前已接受数据的范围,则说明匹配失败
                                if (i + j > dataCount - 1)
                                {
                                    flag = false;
                                    break;
                                }
                                //若分隔符后面的byte与接受数据的相应索引位置的数据不一致,则说明匹配失败
                                if (listRecvByte[i + j] != _resolver.ByteEndTag[j])
                                {
                                    flag = false;
                                    break;
                                }
                            }
                            //若不匹配,继续遍历当前已接受数据
                            if (!flag)
                            {
                                continue;
                            }

                            //说明已匹配上分隔符,去除当前缓存中的尾部分隔符
                            listBufferByte.RemoveRange(listBufferByte.Count - 1, 1);

                            //Need Deep Copy.因为需要保证多个不同报文独立存在 
                            ICloneable copySession = (ICloneable)this;
                            Session clientSession = (Session)copySession.Clone();
                            //发布数据
                            clientSession.Datagram = listBufferByte.ToArray();
                            //去除当前已接受数据中的上述已发布的数据片段,并包括分隔符的去除
                            listRecvByte.RemoveRange(0, i + _resolver.ByteEndTag.Length);
                            //发布一个报文消息
                            if (ReceivedDatagram != null)
                            {

                                ReceivedDatagram(this, new NetEventArgs(clientSession));
                            }
                            break;
                        }
                    }
                }
            }
        }

TcpClient主要代码如下:

/// <summary> 
        /// 连接服务器 
        /// </summary> 
        /// <param name="ip">服务器IP地址</param> 
        /// <param name="port">服务器端口</param> 
        public virtual void Connect(string ip, int port)
        {
            if (IsConnected)
            {
                //重新连接 
                Debug.Assert(_session != null);

                Close();
            }

            Socket newsock = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
            newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
        }
/// <summary> 
        /// 建立Tcp连接后处理过程 
        /// </summary> 
        /// <param name="iar">异步Socket</param> 
        protected virtual void Connected(IAsyncResult iar)
        {
            Socket socket = (Socket)iar.AsyncState;

            socket.EndConnect(iar);

            //创建新的会话 
            _session = new Session(socket, _resolver);
            _session.SessionShutDown += new NetEvent(_session_SessionShutDown);
            _session.ReceivedDatagram += new NetEvent(_session_ReceivedDatagram);
            _isConnected = true;

            //触发连接建立事件 
            if (ConnectedServer != null)
            {
                ConnectedServer(this, new NetEventArgs(_session));
            }
        }

        void _session_ReceivedDatagram(object sender, NetEventArgs e)
        {
            this.ReceivedDatagram(sender, e);
        }

        void _session_SessionShutDown(object sender, NetEventArgs e)
        {
            this.DisConnectedServer(sender, e);
        }

问题是当Socket异步接受到数据,调用方法ReceiveData(IAsyncResult iar)的时候,会将在listRecvByte添加数据,这时候的Notify线程方法查看listRecvByte中是否有完整报文数据,若有,则提取数据并向外发布,但是当Notify方法运行到

 if (ReceivedDatagram != null)
                            {
                                ReceivedDatagram(this, new NetEventArgs(clientSession));
                            }

的时候,有时候能成功触发事件,但有时候却发现ReceivedDatagram事件为null,但是我明明已经在TcpClient类中的Connected方法中已经订阅了事件

 _session.SessionShutDown += new NetEvent(_session_SessionShutDown);
            _session.ReceivedDatagram += new NetEvent(_session_ReceivedDatagram);

为什么会出现这样事件订阅丢失的情况?

我本猿类的主页 我本猿类 | 初学一级 | 园豆:2
提问于:2013-10-31 17:59
< >
分享
所有回答(1)
0
jiulang | 园豆:437 (菜鸟二级) | 2013-10-31 20:42
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册