2013-02-20 105 views
11

我剛剛開始在C#中的Socket編程,現在有點卡住了這個問題。 如何在單個服務器上處理多個客戶端而無需爲每個客戶端創建線程?Socket編程多客戶端一臺服務器

對於每個客戶端的一個線程工作正常時,有說10個客戶端,但如果客戶端號碼上升到1000客戶端創建一個線程爲他們的每一個建議?如果有其他方法可以做到這一點,可以有人請打電話給我?

+1

請嘗試http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod ...如果您搜索'異步套接字c#「,那裏有一百萬篇文章。 – atlaste 2013-02-20 07:55:36

+0

如果你是谷歌的「socket編程C#入門」,有幾百個例子。如果你喜歡這個話題,還可以加載關於這個話題的很棒的視頻視頻。 – MarcF 2013-02-20 12:43:52

回答

19

嘗試使用異步服務器。 以下示例程序將創建一個接收來自客戶端的連接請求的服務器。服務器使用異步套接字構建,因此服務器應用程序的執行在等待客戶端連接時不會掛起。應用程序從客戶端接收字符串,在控制檯上顯示字符串,然後將字符串回顯給客戶端。來自客戶端的字符串必須包含字符串「」來表示消息的結束。

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

這將是最好的解決方案。

+0

我在這個問題中使用了這個解決方案,但由於某種原因,回調不會立即發生,而只會在20秒後發生。你能檢查出來嗎? http://stackoverflow.com/questions/18418613/socket-buffers-the-data-it-receives – 2013-08-27 18:59:45

+0

此解決方案是不完整的,因爲它假定您只有一個網絡適配器。如果您有兩個或兩個以上的網絡適配器,並且想要聽取所有這些解決方案,則此解決方案將失敗。 – 2014-03-11 00:45:45

+0

allDone在回調中不可見。導致你的回調使用靜態修飾符。 – 2016-12-04 13:10:18

1

線程可以很好地工作,但很少能很好地擴展到很多客戶端。有兩種簡單的方法和許多更復雜的方法來處理這個問題,下面是一些僞代碼,說明更容易的兩種方法通常會爲您提供一個概述。

選擇()

這是一個調用來檢查其插座有新的客戶或數據在等待他們,一個典型的程序看起來是這樣的。

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 

這意味着不需要的線程來處理多個客戶端,但它並沒有真正很好地擴展或者如果處理數據需要很長的時間,那麼你就不會到這樣做了讀取新的數據或接受新客戶。

異步插座

這是處理這是上述選擇一種抽象插座的另一種方式。您只需爲常見事件設置回調,並讓框架執行不太重要的操作。

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() 

我認爲如果您的數據處理需要很長時間,這應該會更好地擴展。你會做什麼樣的數據處理?

0

This可能是一個很好的起點。如果你想避免1線程< - > 1客戶端;那麼你應該使用.NET提供的異步套接字工具。核心對象是SocketAsyncEventArgs

相關問題