2014-01-28 136 views
2

我正在编写允许Android客户端连接到C#服务器套接字的代码。客户端和服务器正常工作,但无法关闭或断开套接字。无法关闭异步C#服务器套接字连接

服务器由开始的单击事件:

private void btnStartServer_Click(object sender, EventArgs e) 
{ 
    AsynchronousSocketListener Async = new AsynchronousSocketListener(); 
    receiveThread = new Thread(new ThreadStart(Async.StartListening)); 
    receiveThread.Start(); 

    btnStartServer.Enabled = false; 
    btnStopServer.Enabled = true; 
    MessageBox.Show("Server Started"); 
} 

然后服务器代码的大部分:

// State object for reading client data asynchronously 
public class StateObject 
{ 
    // Client socket. 
    public Socket workSocket = null; 
    // Size of receive buffer. 
    public const int BufferSize = 1024; 
    // Receive buffer. 
    public byte[] buffer = new byte[BufferSize]; 
    // Received data string. 
    public StringBuilder sb = new StringBuilder(); 
} 

public class AsynchronousSocketListener 
{ 
    // Thread signal. 
    public static ManualResetEvent allDone = new ManualResetEvent(false); 

    public AsynchronousSocketListener() 
    { 
    } 

    public void StartListening() 
    { 
     // Data buffer for incoming data. 
     byte[] bytes = new Byte[1024]; 

     // Establish the local endpoint for the socket. 
     IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); 
     IPAddress ipAddress = ipHostInfo.AddressList[0]; 
     IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 3000); 
     System.Diagnostics.Debug.WriteLine(ipAddress); 

     // Create a TCP/IP socket. 
     Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

     // Bind the socket to the local endpoint and listen for incoming connections. 
     try 
     { 
      listener.Bind(localEndPoint); 
      listener.Listen(100); 

      while (true) 
      { 
       // Set the event to nonsignaled state. 
       allDone.Reset(); 

       // Start an asynchronous socket to listen for connections. 
       Console.WriteLine("Waiting for a connection..."); 
       listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 

       Singleton s = Singleton.Instance; 

       if (s.getIsEnded() == false) 
       { 
        // Wait until a connection is made before continuing. 
        allDone.WaitOne(); 
       } 
       else 
       { 
        listener.Shutdown(SocketShutdown.Both); 
        listener.Disconnect(true); 
        break; 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.ToString()); 
     } 
    } 

    public static void AcceptCallback(IAsyncResult ar) 
    { 
     // Get the socket that handles the client request. 
     Socket listener = (Socket) ar.AsyncState; 
     Socket handler = listener.EndAccept(ar); 

     // Create the state object. 
     StateObject state = new StateObject(); 
     state.workSocket = handler; 
     handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); 

     // Signal the main thread to continue. 
     allDone.Set(); 
    } 

    public static void ReadCallback(IAsyncResult ar) 
    { 
     String content = String.Empty; 

     // Retrieve the state object and the handler socket 
     // from the asynchronous state object. 
     StateObject state = (StateObject) ar.AsyncState; 
     Socket handler = state.workSocket; 

     // Read data from the client socket. 
     int bytesRead = handler.EndReceive(ar); 

     if (bytesRead > 0) 
     { 
      // There might be more data, so store the data received so far. 
      state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead)); 

      // Check for end-of-file tag. If it is not there, read 
      // more data. 
      content = state.sb.ToString(); 
      if (content.IndexOf("<EOF>") > -1) 
      { 
       // All the data has been read from the 
       // client. Display it on the console. 
       Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content); 
       if (content.Equals("end<EOF>")) 
       { 
        Console.WriteLine("Should end"); 
        Singleton s = Singleton.Instance; 
        s.setIsEnded(true); 
       } 
       // Echo the data back to the client. 
       Send(handler, content); 
      } 
      else 
      { 
       // Not all data received. Get more. 
       handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
       new AsyncCallback(ReadCallback), state); 
      } 
     } 
    } 

    private static void Send(Socket handler, String data) 
    { 
     // Convert the string data to byte data using ASCII encoding. 
     byte[] byteData = Encoding.ASCII.GetBytes(data); 

     // Begin sending the data to the remote device. 
     handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); 
    } 

    private static void SendCallback(IAsyncResult ar) 
    { 
     try 
     { 
      // Retrieve the socket from the state object. 
      Socket handler = (Socket) ar.AsyncState; 

      // Complete sending the data to the remote device. 
      int bytesSent = handler.EndSend(ar); 
      Console.WriteLine("Sent {0} bytes to client.", bytesSent); 

      handler.Shutdown(SocketShutdown.Both); 
      handler.Close(); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.ToString()); 
     } 
    } 
} 

使用单我可以保持一个独特的变量来检查服务器应该运行或不运行。这在上面的方法StartListening()检查:

public class Singleton 
{ 
    private static Singleton instance; 
    private Boolean isEnded = false; 

    private Singleton() { } 

    public static Singleton Instance 
    { 
     get 
     { 
      if (instance == null) 
      { 
       instance = new Singleton(); 
      } 
      return instance; 
     } 
    } 

    public void setIsEnded(Boolean setter) 
    { 
     isEnded = setter; 
    } 

    public Boolean getIsEnded() 
    { 
     return isEnded; 
    } 
} 

最后已试图通过使用String "end<EOF>"它发送一个消息到停止服务器。服务器逻辑ReadCallback()将通知单身人士设置isEnded = true。这不是一个很好的解决方案,但这是我写作时能够半工半读的唯一方法。断开插座的逻辑在StartListening()。理想情况下,它将断开连接,以便插座可以重新启动。

当我尝试断开连接,然后再次启动插座会出现此错误:

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll 
System.Net.Sockets.SocketException (0x80004005): Only one usage of each socket address (protocol/network address/port) is normally permitted 
    at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) 
    at System.Net.Sockets.Socket.Bind(EndPoint localEP) 
    at StartServer.AsynchronousSocketListener.StartListening() in c:\Users\Conor\Desktop\StartServer\StartServer\StartServer.cs:line 89 

如果我停止服务器,然后试图发送来自Android客户端的字符串,在接收到服务器上的消息,然后在服务器控制台上收到以下消息:

System.Net.Sockets.SocketException (0x80004005): A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied 
    at System.Net.Sockets.Socket.Shutdown(SocketShutdown how) 
    at StartServer.AsynchronousSocketListener.StartListening() 
+0

1.请澄清一下** **断开意味着_this当我尝试断开连接,然后启动插座again_发生错误 - 是它最终''或者是'Singleton.Instance.setIsEnded( true)'在某个button_click处理程序中执行? –

+0

2.另外_如果我停止服务器,然后尝试从Android客户端发送一个字符串 - 你如何阻止它(请参阅previos评论)? –

+0

3.并且_信息在服务器上被接收,然后我在服务器console_上收到以下信息 - 您如何知道收到的信息?你在控制台打印了吗?什么时候是“那么”? –

回答

0

这是一个完整的服务器测试控制台程序。这是容易测试使用Telnet客户端:

远程登录本地主机3000

注意,你将无法看到您键入的内容,但是当你按下输入,所有行会发送到服务器。但是由于服务器回应数据(当它收到<EOF>字符串时),您将在telnet控制台中看到该数据作为响应。

它主要是你的代码。 为了更好地理解事件流,异步Socket使用的多线程性质,我添加了许多* Console.WriteLine * s。

注意的变化在while(true)循环:当服务器收到end<EOF>这不仅会设置IsEnded真正,也将设置ManualResetEvent的,让等待的监听线程疏通并注意已结束标志。

也摆脱了第二SocketException(在Socket.Shutdown)你不会尝试关闭您的监听套接字。仅关闭您读取和写入的套接字。

另请注意AcceptCallback中的try-catch(ObjectDisposedException):使用我的代码,您不可能偶然发现此异常,但在写样本时,我用它来抓取关闭侦听套接字的事件。

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var asyncSocketListener = new AsynchronousSocketListener(); 
     var listenerThread = new Thread(asyncSocketListener.StartListening); 
     listenerThread.Start(); 
     Console.WriteLine("Server Started"); 

     listenerThread.Join(); 
     Console.WriteLine("Press any key to exit..."); 
     Console.ReadKey(); 
    } 

    public class Singleton 
    { 
     private static Singleton instance; 
     private bool isEnded; 

     private Singleton() { } 

     public static Singleton Instance 
     { 
      get 
      { 
       if (instance == null) 
       { 
        instance = new Singleton(); 
       } 
       return instance; 
      } 
     } 

     public bool IsEnded 
     { 
      get { return isEnded; } 
      set { isEnded = value; } 
     } 
    } 

    public class AsynchronousSocketListener 
    { 
     // State object for reading client data asynchronously 
     private class StateObject 
     { 
      // Size of receive buffer. 
      public const int BufferSize = 1024; 
      // Receive buffer. 
      public byte[] Buffer = new byte[BufferSize]; 
      // Client socket. 
      public Socket WorkSocket; 
      // Received data string. 
      public StringBuilder Sb = new StringBuilder(); 
     } 

     // Thread signal. 
     private static ManualResetEvent allDone = new ManualResetEvent(false); 

     public void StartListening() 
     { 
      var localEndPoint = new IPEndPoint(IPAddress.Any, 3000); 
      var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

      // Bind the socket to the local endpoint and listen for incoming connections. 
      try 
      { 
       listener.Bind(localEndPoint); 
       listener.Listen(100); 
       Console.WriteLine("Listening on {0}...", localEndPoint.ToString()); 

       while (true) 
       { 
        allDone.Reset(); 

        if (Singleton.Instance.IsEnded) 
        { 
         Console.WriteLine("Closing listener socket..."); 

         listener.Close(); 
         break; 
        } 

        Console.WriteLine("Waiting for a new connection..."); 
        listener.BeginAccept(AcceptCallback, state: listener); 

        allDone.WaitOne(); 
       } 

       Console.WriteLine("Server stopped."); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.ToString()); 
      } 
     } 

     public static void AcceptCallback(IAsyncResult ar) 
     { 
      Socket clientSocket; 
      try 
      { 
       clientSocket = ((Socket)ar.AsyncState).EndAccept(ar); 
      } 
      catch (ObjectDisposedException) 
      { 
       Console.WriteLine(" Listening socket has been closed."); 
       allDone.Set(); 
       return; 
      } 

      Console.WriteLine(" Connection accepted {0}", clientSocket.RemoteEndPoint.ToString()); 

      var stateObject = new StateObject { WorkSocket = clientSocket }; 
      clientSocket.BeginReceive(stateObject.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, stateObject); 

      // Signal the main thread to continue. 
      Console.WriteLine(" Signal the main thread to accept a new connection"); 
      allDone.Set(); 
     } 

     public static void ReadCallback(IAsyncResult ar) 
     { 
      string content; 

      // Retrieve the state object and the handler socket 
      // from the asynchronous state object. 
      var stateObject = (StateObject)ar.AsyncState; 
      Socket clientSocket = stateObject.WorkSocket; 

      // Read data from the client socket. 
      int bytesRead = clientSocket.EndReceive(ar); 

      if (bytesRead > 0) 
      { 
       // There might be more data, so store the data received so far. 
       stateObject.Sb.Append(Encoding.ASCII.GetString(stateObject.Buffer, 0, bytesRead)); 

       // Check for end-of-file tag. If it is not there, read 
       // more data. 
       content = stateObject.Sb.ToString(); 
       if (content.IndexOf("<EOF>") > -1) 
       { 
        // All the data has been read from the 
        // client. Display it on the console. 
        Console.WriteLine("  Read {0} bytes from socket. \n  Data : {1}", content.Length, content); 
        if (content.Equals("end<EOF>")) 
        { 
         Console.WriteLine("  !!!Should stop the server"); 
         Singleton.Instance.IsEnded = true; 
         allDone.Set(); 
        } 

        // Echo the data back to the client. 
        byte[] byteData = Encoding.ASCII.GetBytes(content); 
        clientSocket.BeginSend(byteData, 0, byteData.Length, 0, WriteCallback, clientSocket); 
       } 
       else 
       { 
        // Not all data received. Get more. 
        clientSocket.BeginReceive(stateObject.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, stateObject); 
       } 
      } 
     } 

     private static void WriteCallback(IAsyncResult ar) 
     { 
      try 
      { 
       // Retrieve the socket from the state object. 
       var clientSocket = (Socket)ar.AsyncState; 

       // Complete sending the data to the remote device. 
       int bytesSent = clientSocket.EndSend(ar); 
       Console.WriteLine("   Sent {0} bytes to client.", bytesSent); 
       Console.WriteLine("   Disconnecting the client..."); 

       clientSocket.Shutdown(SocketShutdown.Both); 
       clientSocket.Close(); 

       Console.WriteLine("   Client disconnected."); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.ToString()); 
      } 
     } 
    } 
} 
+0

不幸的是,我已经尝试过这一点,但它会产生相同的错误。 – Inishkenny

+0

查看我的更新回答。代码主要是你的 - 我只是让它工作。它仍然不是线程安全的,容易出错,甚至可能永远不会遇到)快乐学习) –

+0

一些兴趣点:线程安全的Singleton发布,线程安全的IsEnded标志(内存模型,指令重新排序,易失性),套接字。 *异步方法家庭 –