Исключение Socket ObjectDisposed - PullRequest
       2

Исключение Socket ObjectDisposed

5 голосов
/ 21 августа 2010

Я работаю над небольшой .dll, предназначенной для связи по Tcp,

В моем проекте у меня есть класс сервера, который использует 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);
        }
    }

* Обратите внимание, что Coder - это класс, отвечающий за декодирование и кодирование пакетов данных.

Помимо вышесказанного, у меня есть основанный на сокете 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);
        }
    }

I 'Мы создали набор модульных тестов для обоих.

В одном из тестов я отправляю данные с клиента на сервер.Работает.В другом тесте я отправляю данные из соединения с сервером клиенту.Эпический провал.Я продолжаю получать исключения Socket ObjectDisposed в on_data_received соединения.Честно говоря, я понятия не имею, что происходит - поэтому мне нужна помощь.

Я использую .Net 4, VS 2010, ОС моей машины - Win7 (если эта информация поможет)

С уважением, Maciek

Ответы [ 2 ]

2 голосов
/ 22 августа 2010

Я понял это - наконец.

Проблема выглядела довольно невинно, изучите код ниже.

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 сокет удаляется (я не уверен, что это желаемое поведение) - и, таким образом, разрывает соединение. Удаление пунктов «с помощью» решило проблему.

Размещение ответа здесь на тот случай, если кто-то столкнется с чем-то похожим.

1 голос
/ 21 августа 2010

В вашем обработчике 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 байтов, это означает, что удаленная конечная точка закрылась.Невыполнение этого требования может привести к бесконечному циклу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...