2010-02-23 125 views
1

我看了一些C#聊天源代碼&我看到:在聊天服務器上連接了很多客戶端,服務器監聽器會運行在一個單獨的線程中&每個連接的客戶端也會運行在一個分離的線。 代碼示例:與很多客戶端的聊天服務器

啓動服務器&開始在一個獨立的線程監聽:

public void StartListening() 
     { 

      // Get the IP of the first network device, however this can prove unreliable on certain configurations 
      IPAddress ipaLocal = ipAddress; 

      // Create the TCP listener object using the IP of the server and the specified port 
      tlsClient = new TcpListener(1986); 

      // Start the TCP listener and listen for connections 
      tlsClient.Start(); 

      // The while loop will check for true in this before checking for connections 
      ServRunning = true; 

      // Start the new tread that hosts the listener 
      thrListener = new Thread(KeepListening); 
      thrListener.Start(); 
     } 

private void KeepListening() 
     { 
      // While the server is running 
      while (ServRunning == true) 
      { 
       // Accept a pending connection 
       tcpClient = tlsClient.AcceptTcpClient(); 
       // Create a new instance of Connection 
       Connection newConnection = new Connection(tcpClient); 
      } 
     } 

和連接也將在一個獨立的線程運行:

public Connection(TcpClient tcpCon) 
     { 
      tcpClient = tcpCon; 
      // The thread that accepts the client and awaits messages 
      thrSender = new Thread(AcceptClient); 
      // The thread calls the AcceptClient() method 
      thrSender.Start(); 
     } 

所以,如果有一個聊天服務器10000個連接的客戶端,聊天服務器應用程序將有10002個線程(一個主線程,一個服務器線程& 10000個客戶端線程)。我認爲聊天服務器會佔用大量的線程。請幫我解決。謝謝。

UPDATE: 我相信聊天的例子只是爲了學習網絡&它們不適合於現實世界的模型。請給我一個真實世界的解決方案。謝謝。

+1

如果你期待10,000個客戶端,你會有其他問題(帶寬,爲一) – SLaks 2010-02-23 03:37:17

+3

請參閱http://stackoverflow.com/questions/869744/how-to-write-a-scalable-tcp-ip-based-server – Jimmy 2010-02-23 03:38:21

回答

1

如果您使用.Net Framework 2.0 SP2或更高版本,則可能會使用基於IO Completion ports的新異步套接字模型,因此您不應創建自己的線程,因爲IO Completion端口做所有的工作適合你

下面一些例子:

tcpServer = new System.Net.Sockets.TcpListener(IPAddress.Any, port); 
tcpServer.Start(); 
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer); 


private void EndAcceptSocket(IAsyncResult asyncResult) 
{ 
    TcpListener lister = (TcpListener)asyncResult.AsyncState; 
    Socket sock = lister.EndAcceptSocket(asyncResult); 
    //handle socket connection (you may add socket to you internal storage or something) 

    //start accepting another sockets 
    lister.BeginAcceptSocket(EndAcceptSocket, lister); 


    SocketAsyncEventArgs e = new SocketAsyncEventArgs(); 
    e.Completed += ReceiveCompleted; 
    e.SetBuffer(new byte[socketBufferSize], 0, socketBufferSize); 
    sock.ReceiveAsync(e); 
} 


void ReceiveCompleted(object sender, SocketAsyncEventArgs e) 
{ 
    var sock = (Socket)sender; 
    if (!sock.Connected) 
    { 
     //handle socket disconnection 
    } 
    var buf = new byte[size]; 
    Array.Copy(e.Buffer, buf, size); 
    //handle received data 

    //start reading new data 
    sock.ReceiveAsync(e); 
} 
0

爲了讓事情變得更糟,您還需要在一些任意數量的線程之間進行通信(這是一個聊天服務器,人們希望彼此交談,而不是自己)。我建議尋找UDP - 可以用服務器上的單線程並且適合網絡活動 - 人們很少在聊天交換中多次寫幾個句子,這對於限制大小的UDP數據報非常方便。

當然還有其他的方法,但一個確定的事情是,你將永遠無法在每個套接字上執行線程。

+0

你的意思是簡單的聊天源代碼不適用於真實世界的模型。 – 2010-02-23 09:06:49

0

1)你永遠不會想要運行多個線程 - 即使你可以讓它們在你的機器上運行(你不能 - 每個線程都有一個與它關聯的堆棧,它需要真正的RAM,並且當你開始越來越多,你會用盡你的盒子裏的物理資源,看着它爆炸)。

2)你會想看看線程池 - 使用較少的線程來處理大量的工作 - 通常從你想盡快完成的工作隊列中讀取。

3)您將需要查看io完成端口 - 當io(如磁盤讀取或網絡io)等待您採取行動時進行回調的方式 - 考慮線程(或池的線程)致力於獲取io通知,然後推動爲該io進入隊列的行動,然後再處理實際消息/日誌記錄等的另一線程池。

4)當您擴展到一臺機器之外會發生什麼?如果你成功了,你希望做什麼? :-)通常,人們將一組N個機器專用於聊天 - 然後根據用戶的標識符(認爲代表用戶的GUID - 或者UserID/bigint取決於與某些內部認證令牌相對應的內容從登錄到登錄),這允許他們確定性地將用戶的狀態/狀態信息發送到專用於消息收發的N個框中的特定機器。因此,如果與服務器N [2]哈希的用戶需要檢查是否有朋友登錄,那麼很容易知道他們的每個朋友確切地知道他們的朋友的狀態應該在哪個機器中,因爲後端一直將這些朋友哈希到IM與每個userid散列對應的機器。 (也就是說,你只需從userid中知道服務器場中的哪個服務器應該處理該用戶的IM狀態。)

只是不認爲你會旋轉一堆線程,這將節省一天。只適用於非常小的數字

0

我建議您在MSDN雜誌上閱讀this great article。 描述:

  • 線程服務器
  • 選擇基於服務器的
  • 異步服務器在C#

碼& VB.Net