套接字对象已释放异常

5

我正在开发一个专门用于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's on_data_received中接收Socket ObjectDisposed异常。说实话,我不知道发生了什么事情 - 因此我需要一些帮助。
我正在使用 .Net 4、VS 2010,我的机器操作系统是Win7(如果这些信息有所帮助)。
谢谢, Maciek
2个回答

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)); }
        }

当使用关键字“using”处理BinaryWriter或NetworkStream时,套接字将被处理(我不确定这是否是期望的行为),从而中断连接。移除“using”语句可以解决这个问题。
在此发布答案,以防其他人遇到类似情况。

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字节时,应关闭连接,这意味着远程端点已关闭。不这样做可能会导致无限循环。

这是一个有道理的观点,但它并没有解决主要问题。服务器--数据-->客户端 :( 仍在努力完成此操作。 - Maciek
“Note: 当接收到0字节时,您应该关闭连接”关于这一点说得很好。 - Marco Mp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接