2014-03-12 46 views
2

對於這個問題的目的,我做了一個超級簡單的例子:的TcpListener服務器循環空轉,直到有一個掛起的連接

using System; 
using System.Net; 
using System.Net.Sockets; 

namespace Loop 
{ 
    class Program 
    { 
     public static void Main (string[] args) 
     { 
      TcpListener server = new TcpListener(IPAddress.Any, 1337); 

      server.Start(); 

      Console.WriteLine("Starting listener on {0}:{1}", IPAddress.Any, 1337); 

      while (true) 
      { 
       if (server.Pending()) 
       { 
        Console.WriteLine("Activity..."); 

        Socket client = server.AcceptSocket(); 

        IPEndPoint clientAddress = (IPEndPoint) client.RemoteEndPoint; 

        Console.WriteLine("Accepted client: {0}:{1}", clientAddress.Address, clientAddress.Port); 

        client.Close(); 

        Console.WriteLine("Closed connection to: {0}:{1}", clientAddress.Address, clientAddress.Port); 
       } 
       else 
       { 
        // Currently takes 100% of my CPU (well, actually Core - 25%, but you get the idea). 
        // How do I idle (CPU @ 0%) the loop until pending connection? 
       } 
      } 
     } 
    } 
} 

註釋已經包含的問題,但是,是的,我怎麼閒着循環,直到有一個實際的掛起連接,以便我的CPU沒有融化?

當給定套接字上存在未決連接時,是否有方法僅在OS事件上偵聽和喚醒? (喜歡的東西libuv (node.js)做,這就是爲什麼我添加

我實際的實現意味着一個相當基本的反應堆事件循環,但是,是的,我不知道如何傾聽與C#OS事件(如果有任何可能性)。

我知道BeginAccept和另一堆異步系列,但這些人由於他們的多線程性質而不是可以接受的。

此外,我知道我可以簡單地在循環內Thread.Sleep,但我正在尋找基於事件的行爲。

P.S.我正在使用Mono,目標是Linux可執行文件。

+0

如果要使用阻塞樣式,可以先調用AcceptSocket而不等待第一個等待;該呼叫將阻止(不旋轉),直到連接被接受。或者,您可以在輪詢循環中添加一個Sleep來減少CPU開銷,但編寫網絡I/O代碼以避免輪詢並使用正確的同步工具僅在I/O可用時喚醒時會更好。 –

+0

我只想指出'BeginAccept'不一定是多線程的(至少就你的代碼而言)。如果你提供了一個同步上下文,那麼如果你有權訪問'await' /'async','AcceptSocketAsync'非常容易使用,並且它將異步地處理事物,同時使用與同步非常相似的代碼,並完全解決多線程問題(因爲你實際上在你的代碼中一直在處理一個線程)。 Mono現在應該支持'await' /'async'。 – Luaan

+0

@Luaan,多線程我認爲任何東西,每個請求產生額外的線程。 – jolt

回答

2

您正在使用的機制(Pending)如果你想避免等待可用,然後有一個問題,因爲你想等待:

 while (true) 
     { 
       Socket client = server.AcceptSocket(); 

       Console.WriteLine("Activity..."); 

       IPEndPoint clientAddress = (IPEndPoint) client.RemoteEndPoint; 

       Console.WriteLine("Accepted client: {0}:{1}", clientAddress.Address, clientAddress.Port); 

       client.Close(); 

       Console.WriteLine("Closed connection to: {0}:{1}", clientAddress.Address, clientAddress.Port); 
     } 

AcceptSocket塊,直到它得到一個連接。

通常情況下,獲取連接後,你手上的開放插座關閉到別的東西(一個專門的線程或線程池,或別的東西),只是回到身邊再打電話AcceptSocket所以一次有多個客戶端可以連接。

每個套接字一個線程並不能很好地擴展,但在您只玩遊戲時它可以正常工作。

+0

什麼是一個更合適的技術,而不是每個線程使用一個套接字?每個線程是否有多個套接字是合理的?這個數字是否改變取決於你有多少連接活動? – TheMonarch

+0

@TheMonarch - 現在我通常會推薦使用更高級別的抽象,比如異步機制和/或任務,並讓其他*擔心管理線程。 –

3

我建議你只是刪除檢查掛起的連接,讓主線程塊(AcceptSocket是一個阻塞方法),直到連接被接受:

while (true) 
{ 
    Console.WriteLine("Waiting for client to connect..."); 

    Socket client = server.AcceptSocket(); // This is a blocking call... 

    Console.WriteLine("Client connected..."); 

    IPEndPoint clientAddress = (IPEndPoint) client.RemoteEndPoint; 

    Console.WriteLine("Accepted client: {0}:{1}", clientAddress.Address, clientAddress.Port); 

    client.Close(); 

    Console.WriteLine("Closed connection to: {0}:{1}", clientAddress.Address, clientAddress.Port); 
} 
-1

循環會以最快的速度去可能 - 這就是爲什麼它佔用了你很多的CPU週期。

嘗試在其他人的身上加入

System.Threading.Thread.Sleep(10); 

以上將解決高CPU負載,但它不會解決您在調用Sleep()方法時發生在主線程上的阻塞。我個人建議在新的(IsBackground = true)線程下運行你的服務器循環。這會導致CPU週期使用率更低,並防止主線程被阻塞。

我希望我能回答你的問題。

+0

這是治療症狀(高CPU負荷)。更好地對待原因:待定呼叫。 – usr