2013-02-20 8 views
11

Ich beginne gerade Socket-Programmierung in C# und bin jetzt ein bisschen mit diesem Problem fest. Wie behandeln Sie mehrere Clients auf einem einzelnen Server, ohne für jeden Client einen Thread zu erstellen?Sockel Programmierung mehrerer Client 1 Server

Der eine Thread für jeden Client funktioniert gut, wenn es 10 Clients gibt, aber wenn die Client-Nummer auf 1000 Clients ansteigt, wird ein Thread für jeden einzelnen von ihnen empfohlen? Wenn es eine andere Methode gibt, kann jemand mich bitte anrufen?

+1

Versuchen Sie http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod ... es gibt eine Million Artikel um dort, wenn Sie nach 'async Socket C#' suchen .. – atlaste

+0

Es gibt Hunderte von Beispielen, wenn Sie googlen "Einführung in Socket-Programmierung C#". Auch viele tolle You-Tube-Videos zum Thema, wenn Sie das bevorzugen. – MarcF

Antwort

19

Versuchen Sie den asynchronen Server zu verwenden. Das folgende Beispielprogramm erstellt einen Server, der Verbindungsanforderungen von Clients empfängt. Der Server verfügt über einen asynchronen Socket, sodass die Ausführung der Serveranwendung nicht ausgesetzt wird, während auf eine Verbindung von einem Client gewartet wird. Die Anwendung empfängt eine Zeichenfolge vom Client, zeigt die Zeichenfolge in der Konsole an und gibt die Zeichenfolge dann an den Client zurück. Die Zeichenfolge vom Client muss die Zeichenfolge "" enthalten, um das Ende der Nachricht zu signalisieren.

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

    // 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 static void StartListening() { 
      // Data buffer for incoming data. 
      byte[] bytes = new Byte[1024]; 

      // Establish the local endpoint for the socket. 
      // The DNS name of the computer 
      // running the listener is "host.contoso.com". 
      IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); 
      IPAddress ipAddress = ipHostInfo.AddressList[0]; 
      IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000); 

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

        // Wait until a connection is made before continuing. 
        allDone.WaitOne(); 
       } 

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

      Console.WriteLine("\nPress ENTER to continue..."); 
      Console.Read(); 

     } 

     public static void AcceptCallback(IAsyncResult ar) { 
      // Signal the main thread to continue. 
      allDone.Set(); 

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

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

    public static int Main(String[] args) { 
     StartListening(); 
     return 0; 
    } 
} 

Das ist die beste Lösung.

+0

Ich verwende diese Lösung in dieser Frage, aber aus irgendeinem Grund erfolgt der Rückruf nicht sofort, sondern nur etwa 20 Sekunden später. Kannst du es dir anschauen? http://stackoverflow.com/questions/18418613/socket-buffers-the-data-it-empfangen –

+0

Diese Lösung ist unvollständig, da davon ausgegangen wird, dass Sie nur einen Netzwerkadapter haben. Wenn Sie zwei oder mehr Netzwerkadapter haben und Sie alle hören möchten, schlägt diese Lösung fehl. –

+0

AllDone ist im Callback nicht sichtbar. Ursache der Callback verwendet einen statischen Modifikator. –

1

Threads können gut funktionieren, skalieren aber selten gut zu vielen Clients. Es gibt zwei einfache Wege und viele komplexere Wege, das zu handhaben, hier ist ein Pseudocode, wie die leichteren zwei normalerweise strukturiert sind, um Ihnen einen Überblick zu geben.

select()

Dies ist ein Aufruf zu überprüfen, an denen, die Steckdosen haben neue Kunden oder Daten warten, ein typisches Programm sieht dies so etwas wie.

server = socket(), bind(), listen() 
while(run) 
    status = select(server) 
    if has new client 
     newclient = server.accept() 
     handle add client 
    if has new data 
     read and handle data 

Was bedeutet, werden keine Threads benötigt, mehrere Clients zu handhaben, aber es ist nicht wirklich gut skaliert, wenn entweder Griff Daten eine lange Zeit in Anspruch nehmen, dann werden Sie keine neuen Daten lesen oder neue Kunden akzeptieren, bis das erledigt ist .

Async Steckdosen

Dies ist ein weiterer Weg, Steckdosen der Handhabung, die Art von oben wählen Sie abstrahiert wird. Sie richten nur Callbacks für häufige Ereignisse ein und lassen das Framework das nicht so schwere Heben durchführen.

function handleNewClient() { do stuff and then beginReceive(handleNewData) } 
function handleNewData() { do stuff and then beginReceive(handleNewData) } 
server = create, bind, listen etc 
server.beginAddNewClientHandler(handleNewClient) 
server.start() 

Ich denke, dass sollte besser skalieren, wenn Ihre Datenverarbeitung eine lange Zeit braucht. Welche Art von Datenverarbeitung werden Sie durchführen?

0

This könnte ein guter Ausgangspunkt sein. Wenn Sie 1 Thread vermeiden wollen < -> 1 Client; dann sollten Sie asynchrone Socket-Funktionen in .NET verwenden. Kernobjekt, das hier verwendet werden soll, ist SocketAsyncEventArgs.