2010-09-02 2 views
0

Im пишу приложение, которое будет выступать в качестве слушателя tcp для одного клиента. Клиент представляет собой Java-апплет и будет периодически подключаться к слушателю, отправлять некоторые данные и ждать ответа.Асинхронное гнездо, принудительное отключение и повторное использование

Код для класса TcpServer, приведенный ниже, был в значительной степени снят с примера, предоставленного более осведомленным элементом stackoverflow в ответ на другой вопрос.

Все прошло отлично, пока я не нашел что-то в тестировании, которое не упоминалось ни в одном из документов интерфейса, которые у меня есть. После того, как сервер ответил клиенту, он должен отключить клиента и снова начать прослушивание.

Моя первая мысль заключалась в том, чтобы вызвать Disconnect() из SendData(), но это приводит к вызову 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

Всякий раз, когда я пишу код, который обтекает System.Net.Sockets.Socket, я считаю себя постоянно добавляя попытки/уловах положения для SocketException и ObjectDisposedException. В большинстве случаев ObjectDisposedException можно просто игнорировать, так как он в 99% случаев указывает, что клиент просто отключился.

По крайней мере, это мое представление о том, как работает Socket API в .NET. Попробуйте добавить некоторые обработчики исключений здесь и там и посмотреть, как это происходит. В любом случае, ваш метод 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. 
    } 

Я надеюсь, что проливает некоторый свет на вопрос ...

Смежные вопросы