异步套接字,强制断开和重用

时间:2010-09-02 16:25:19

标签: c# sockets asynchronous tcp

我正在编写一个应用程序,它将作为单个客户端的tcp侦听器。客户端是一个java applet,它会定期连接到监听器,发送一些数据然后等待响应。

以下TcpServer类的代码已基本上从一个知识渊博的stackoverflow成员提供的示例中提取,以回应不同的问题。

一切都很顺利,直到我发现测试中的内容在我所拥有的任何界面文档中都没有提到。在服务器响应客户端之后,它必须断开客户端并再次开始监听。

我的第一个想法是从SendData()内部调用Disconnect(),但是这导致从某个地方调用ReceiveCompleted(),并且关于已经处理的套接字有一个令人讨厌的异常。

使用我的代码设计是否可以轻松实现此要求,并且在重新使用套接字进行后续连接时会遇到任何问题吗?

sealed class TcpServer : IDisposable
    {
        #region Fields

        private const int SocketBufferSize = 1024;

        private readonly TcpListener tcpListener;

        private Socket connectedSocket;
        private bool disposed = false;

        #endregion Fields

        #region Constructors

        public TcpServer(int port)
        {
            tcpListener = new TcpListener(IPAddress.Any, port);
            tcpListener.Start();
            tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
        }

        ~TcpServer()
        {
            Dispose(false);
        }

        #endregion Constructors

        #region Events

        public event EventHandler<DataReceivedEventArgs> DataReceived;

        public event EventHandler<IPEndPointEventArgs> SocketConnected;

        public event EventHandler<IPEndPointEventArgs> SocketDisconnected;

        #endregion Events

        #region Methods

        public void Dispose()
        {
            Dispose(true);
        }

        public void SendData(byte[] data)
        {
            if (connectedSocket == null)
            {
                return;
            }
            connectedSocket.Send(data);
        }

        private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e)
        {
            if (!sock.ReceiveAsync(e))
            {
                ReceiveCompleted(sock, e);
            }
        }

        private void Connected(Socket socket)
        {
            var endPoint = (IPEndPoint)socket.RemoteEndPoint;

            connectedSocket = socket;

            OnSocketConnected(endPoint);
        }

        private void Disconnect(Socket socket)
        {
            var endPoint = (IPEndPoint)socket.RemoteEndPoint;

            socket.Close();

            connectedSocket = null;

            OnSocketDisconnected(endPoint);

            tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
        }

        private void Dispose(bool disposing)
        {
            if (this.disposed == false)
            {
                if (disposing)
                {
                    try
                    {
                        if (tcpListener != null)
                        {
                            this.disposed = true;
                            tcpListener.Stop();
                        }
                    }
                    catch (Exception ex)
                    {
                        TraceLog.Error("TcpServer: tcpListener.Stop(): {0}", ex.Message);
                    }

                    try
                    {
                        if (connectedSocket != null)
                        {
                            connectedSocket.Close();
                            connectedSocket = null;
                        }
                    }
                    catch (SocketException ex)
                    {
                        TraceLog.Error("TcpServer: connectedSocket.Close(): {0}", ex);
                    }
                }
                this.disposed = true;
            }
        }

        private void EndAcceptSocket(IAsyncResult asyncResult)
        {
            var listener = (TcpListener)asyncResult.AsyncState;

            if (disposed)
            {
                return;
            }

            try
            {
                Socket sock = listener.EndAcceptSocket(asyncResult);
                Connected(sock);

                var e = new SocketAsyncEventArgs();
                e.Completed += ReceiveCompleted;
                e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
                BeginReceiveAsync(sock, e);
            }
            catch (SocketException ex)
            {
                TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
            }
            catch (Exception ex)
            {
                TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
            }
        }

        private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint)
        {
            if (DataReceived != null)
            {
                DataReceived(this, new DataReceivedEventArgs(data, ipEndPoint));
            }
        }

        private void OnSocketConnected(IPEndPoint ipEndPoint)
        {
            if (SocketConnected != null)
            {
                SocketConnected(this, new IPEndPointEventArgs(ipEndPoint));
            }
        }

        private void OnSocketDisconnected(IPEndPoint ipEndPoint)
        {
            if (SocketDisconnected != null)
            {
                SocketDisconnected(this, new IPEndPointEventArgs(ipEndPoint));
            }
        }

        private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
        {
            var sock = (Socket)sender;

            if (!sock.Connected)
            {
                Disconnect(sock);
            }

            try
            {
                int size = e.BytesTransferred;
                if (size == 0)
                {
                    Disconnect(sock);
                }
                else
                {
                    var buf = new byte[size];
                    Array.Copy(e.Buffer, buf, size);
                    ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint);
                    BeginReceiveAsync(sock, e);
                }
            }
            catch (SocketException ex)
            {
                TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
            }
            catch (Exception ex)
            {
                TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
            }
        }

        private void ReceiveData(byte[] data, IPEndPoint endPoint)
        {
            OnDataReceived(data, endPoint);
        }

        #endregion Methods
    }

1 个答案:

答案 0 :(得分:1)

每当我编写包装System.Net.Sockets.Socket的代码时,我发现自己不断为SocketException和ObjectDisposedException添加try / catch子句。在大多数情况下,可以简单地忽略ObjectDisposedException,因为在99%的情况下,它表示客户端已经断开连接。

至少这是我对.NET中Socket API如何工作的印象。尝试在这里和那里添加一些异常处理程序,看看它是怎么回事。在任何情况下,您的Disconnect方法都不应该执行以下操作:

    public void Disconnect()
    {
        try
        {
            connectedSocket.Shutdown(SocketShutdown.Both);
        }
        catch (Exception)
        {
            // Ignore the exception. The client probably already disconnected.
        }

        connectedSocket.Dispose(); // This is safe; a double dispose will simply be ignored.
    }

我希望能够对这个问题有所了解......