Socket ObjectDisposedException

时间:2010-08-21 11:52:40

标签: c# sockets

我正在开发一个专门用于Tcp通信的小型.dll,

在我的项目中,我有一个使用TcpListener接受传入连接的服务器类。 传入连接存储在字典中并从那里进行处理。

每个连接的代码如下:

 public class Connection : ConnectionBase<Coder.Coder>
    {
        public Connection(TcpClient client, Guid id) : base()
        {
            Id = id;
            Client = client;
        }

        public void Start()
        {
            IsConnected = true;            
            Client.Client.BeginReceive(m_message, 0, m_message.Length, SocketFlags.None, new AsyncCallback(on_data_received), null);            
        }

        public void Stop()
        {
            try
            { 
                Client.Close();
                handle_connection_lost(new ConnectionLostArgs(Id));
            }
            catch
            { }
        }

        public void Send(byte[] data)
        {
            try
            {
                using (NetworkStream s = Client.GetStream())
                {
                    using (BinaryWriter w = new BinaryWriter(s))
                    {
                        var buffer = m_coder.Encode(data);
                        w.Write(buffer);
                        w.Flush();
                    }
                }

            }
            catch
            { handle_connection_lost(new ConnectionLostArgs(Id)); }
        }

        public Guid Id { get; set; }
        public TcpClient Client { get; set; }

        private byte[] m_message = new byte[1024];        

        private void on_data_received(IAsyncResult ar)
        {
            try
            {
                Client.Client.BeginReceive(m_message, 0, m_message.Length,
                        SocketFlags.None, new AsyncCallback(on_data_received), null);

                int bytesRead = Client.Client.EndReceive(ar);

                if (bytesRead > 0)
                {
                    byte[] data = new byte[bytesRead];
                    Array.Copy(m_message, data, bytesRead);

                    m_coder.Push(data);

                }               
            }
            catch(Exception ex)
            {
                Console.WriteLine("Connection::on_data_received : {0}", ex.Message);
                handle_connection_lost(new ConnectionLostArgs(Id));
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                try
                {
                    Stop();
                }
                catch
                { }
            }

            base.Dispose(disposing);
        }
    }

*请注意,编码器是一个负责解码和编码数据包的类。

除了上述内容之外,我还有一个基于套接字的TcpClient(我希望稍后可以在Silverlight中重用),代码如下:

 public class TcpSocketClient : TcpClientBase<Coder.Coder>
    {
        public static TcpSocketClient Create(string host, int port)
        {            
            if (port == 0)
                return null;

            return new TcpSocketClient(host, port);
        }

        private TcpSocketClient(string host, int port) : base()
        {
            IsConnected = false;
            RemoteEndpoint = new DnsEndPoint(host, port);
        }

        public void Start()
        {
            m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            byte[] buffer = new byte[1024];

            SocketAsyncEventArgs e = new SocketAsyncEventArgs()
            {
                RemoteEndPoint = RemoteEndpoint,
                UserToken = m_socket,

            };
            e.SetBuffer(buffer, 0, buffer.Length);
            e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_connect_completed);

            m_socket.ConnectAsync(e);
        }        

        public void Stop()
        {
            try
            {
                m_socket.Close();
                m_socket.Dispose();
            }
            catch (ObjectDisposedException)
            { }
        }

        public void Send(byte[] data)
        {
            try
            {
                var buffer = m_coder.Encode(data);
                SocketAsyncEventArgs e = new SocketAsyncEventArgs()
                {
                    BufferList = new List<ArraySegment<byte>>() { new ArraySegment<byte>(buffer) },
                    UserToken = m_socket
                };
                m_socket.SendAsync(e);                
            }
            catch (Exception ex)
            { handle_client_disconnected(ex.Message); }
        }

        #region Properties
        public DnsEndPoint RemoteEndpoint { get; private set; }
        #endregion

        #region Fields
        Socket m_socket;
        #endregion

        void handle_socket_connect_completed(object sender, SocketAsyncEventArgs e)
        {
            if (!m_socket.Connected)
            {
                handle_client_disconnected("Failed to connect");
                return;
            }


            e.Completed -= handle_socket_connect_completed;
            e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_async_receive);

            handle_client_connected();

            m_socket.ReceiveAsync(e);                        
        }

        void handle_socket_async_receive(object sender, SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred == 0)
            {
                handle_client_disconnected("Connection closed by the remote host");
                try { m_socket.Close(); }
                catch { }
                return;
            }

            try 
            {
                byte[] buffer = new byte[e.BytesTransferred];
                Array.Copy(e.Buffer, buffer, e.BytesTransferred);
                m_coder.Push(buffer);                
            }
            catch { }


            m_socket.ReceiveAsync(e);            
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                try
                {
                    RemoteEndpoint = null;
                    m_socket.Close();
                    m_socket.Dispose();
                }
                catch
                { }
            }

            base.Dispose(disposing);
        }
    }

我为两者创建了一组单元测试。

在其中一个测试中,我将数据从客户端发送到服务器。作品。 在另一个测试中,我将数据从服务器的连接发送到客户端。史诗失败。 我一直在Connection的on_data_received中获得Socket ObjectDisposed异常。说实话,我不知道发生了什么 - 因此我需要一些帮助。

我正在使用.Net 4,VS 2010,我的机器操作系统是Win7(如果此信息有任何帮助)

此致 Maciek

2 个答案:

答案 0 :(得分:2)

我终于明白了。

问题看起来很无辜,请检查下面的代码。

public void Send(byte[] data)
        {
            try
            {
                using (NetworkStream s = Client.GetStream())
                {
                    using (BinaryWriter w = new BinaryWriter(s))
                    {
                        var buffer = m_coder.Encode(data);
                        w.Write(buffer);
                        w.Flush();
                    }
                }

            }
            catch
            { handle_connection_lost(new ConnectionLostArgs(Id)); }
        }

当处理(thx到using关键字)时,BinaryWriter或NetworkStream套接字将被释放(我不确定这是否是所需的行为) - 从而中断连接。删除“使用”子句解决了这个问题。

在此处发布答案,以防其他人遇到类似的事情。

答案 1 :(得分:1)

on_data_received处理程序中,您在Client.Client.BeginReceive(...)之前致电Client.Client.EndReceive(...)

BeginReceive可以同步完成,导致异常并处理你的Connection,所以你应该在EndReceive之后调用它。

    private void on_data_received(IAsyncResult ar)
    {
        try
        {
            int bytesRead = Client.Client.EndReceive(ar);

            if (bytesRead > 0)
            {
                byte[] data = new byte[bytesRead];
                Array.Copy(m_message, data, bytesRead);

                m_coder.Push(data);

                Client.Client.BeginReceive(m_message, 0, m_message.Length,
                    SocketFlags.None, new AsyncCallback(on_data_received), null);
            }
            else
            {
                //TODO Close the connection
            }

        }
        catch(Exception ex)
        {
            Console.WriteLine("Connection::on_data_received : {0}", ex.Message);
            handle_connection_lost(new ConnectionLostArgs(Id));
        }
    }

注意:接收0字节时应关闭连接,这意味着远程端点已关闭。不这样做可能会导致无限循环。