2008-09-15 3 views
14

Bei der Verwendung des Net.Sockets.TcpListener, wie können eingehende Verbindungen (. AcceptSocket) in separaten Threads am besten behandelt werden?Wie tcplistener eingehende Verbindungen über Threads in .NET verteilt werden?

Die Idee besteht darin, einen neuen Thread zu starten, wenn eine neue eingehende Verbindung akzeptiert wird, während der Tcplistener für weitere eingehende Verbindungen verfügbar bleibt (und für jede neue eingehende Verbindung ein neuer Thread erstellt wird). Die gesamte Kommunikation und Beendigung mit dem Client, der die Verbindung herstellte, wird im Thread behandelt.

Beispiel C# von VB.NET-Code wird geschätzt.

Antwort

15

Der Code, den ich habe sieht wie folgt aus:

class Server 
{ 
    private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false); 

    public void Start() 
    { 
    TcpListener listener = new TcpListener(IPAddress.Any, 5555); 
    listener.Start(); 

    while(true) 
    { 
     IAsyncResult result = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener); 
     connectionWaitHandle.WaitOne(); // Wait until a client has begun handling an event 
     connectionWaitHandle.Reset(); // Reset wait handle or the loop goes as fast as it can (after first request) 
    } 
    } 


    private void HandleAsyncConnection(IAsyncResult result) 
    { 
    TcpListener listener = (TcpListener)result.AsyncState; 
    TcpClient client = listener.EndAcceptTcpClient(result); 
    connectionWaitHandle.Set(); //Inform the main thread this connection is now handled 

    //... Use your TcpClient here 

    client.Close(); 
    } 
} 
+0

Danke für den Quellcode, ich werde es so codieren. Neue Threads können teuer sein, aber da ich nicht mehr als 5 oder 6 gleichzeitige eingehende Verbindungen skalieren kann, wird dies für jetzt in Ordnung sein. –

+1

Think Listener und TcpListener wurde in Beispiel verwirrt, sonst guten Code. Gefunden diese: http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.beginaccepttcpclient.aspx basierend auf was ich hier gelehnt. –

+0

Aktueller Code ist sehr gefährlich im Performance-Kontext Ich werde es bearbeiten ... – Beygi

0

Ich würde einen Threadpool verwenden, auf diese Weise müssen Sie nicht jedes Mal einen neuen Thread starten (da dies ein bisschen teuer ist). Ich würde auch nicht für weitere Verbindungen unbestimmt warten, da Kunden ihre Verbindungen nicht schließen können. Wie planen Sie, den Client jedes Mal an denselben Thread weiterzuleiten?

Entschuldigung, habe keine Probe.

3

herunterladen Ich glaube, Sie tun es auf die gleiche Weise wie jede andere asynchrone Operation in .NET: Sie rufen die BeginXxx-Version der Methode, in diesem Fall BeginAcceptSocket. Ihr Rückruf wird im Thread-Pool ausgeführt.

Gepoolte Threads skalieren im Allgemeinen viel besser als Threads pro Verbindung: Sobald Sie einige Dutzend Verbindungen überwunden haben, arbeitet das System beim Wechseln zwischen Threads viel härter als beim Ausführen der eigentlichen Arbeit. Zusätzlich hat jeder Thread seinen eigenen Stack, der typischerweise 1 MB groß ist (obwohl er von Link-Flags abhängt), der im virtuellen 2-GB-Adressraum (auf 32-Bit-Systemen) gefunden werden muss; In der Praxis beschränkt dies Sie auf weniger als 1000 Threads.

Ich bin nicht sicher, ob .NET Threadpool es derzeit verwendet, aber Windows hat ein Kernel-Objekt namens I/O Completion Port, die in skalierbare E/A hilft. Sie können diesem Objekt Threads zuordnen, und E/A-Anforderungen (einschließlich der Annahme eingehender Verbindungen) können damit verknüpft werden. Wenn ein E/A abgeschlossen ist (z. B. eine Verbindung eintrifft), gibt Windows einen wartenden Thread frei, jedoch nur, wenn die Anzahl der aktuell ausführbaren Threads (aus einem anderen Grund nicht blockiert) kleiner als das konfigurierte Skalierbarkeitslimit für den Completion-Port ist. Normalerweise würden Sie dies auf ein kleines Vielfaches der Anzahl der Kerne einstellen.

2

Ich möchte einen anderen Ansatz vorschlagen: Mein Vorschlag verwendet nur zwei Threads. * Ein Thread prüft auf eingehende Verbindungen. * Wenn eine neue Verbindung geöffnet wird, wird diese Information in eine gemeinsame Datenstruktur geschrieben, die alle aktuellen offenen Verbindungen enthält. * Der zweite Thread zählt die Datenstruktur auf und empfängt für jede offene Verbindung gesendete Daten und sendet Antworten.

Diese Lösung ist threadseitig besser skalierbar und sollte bei korrekter Implementierung eine bessere Leistung haben, als einen neuen Thread pro geöffneter Verbindung zu öffnen.

+0

Das Ich werde mich erinnern, obwohl ich wirklich sofort jede eingehende Verbindung antworten möchte. Danke für den Vorschlag. –

+0

Hallo! @Dror Helper, dein Vorschlag sieht wie gut aus.So implementiert man eine Effizienzstrategie? Können Sie teilen, haben Sie ein Code-Snippet? – bashkan