2016-04-07 6 views
1

So habe ich Socket-Programmierung und lesen auf Möglichkeiten zum Erstellen von Hochleistungsservern.Socket Listener Mehrere gleichzeitige AcceptAsync-Aufrufe

Lesen über die AcceptAsync Methode von Socket und wie manchmal die Aufrufe syncronous ausführen (und false zurückgeben), wenn Verbindungen sofort verfügbar sind, und wie den Socket wieder in einen hörenden Zustand nach dem Akzeptieren einer Verbindung setzen, fing ich an frage mich, ob das wirklich bedeutete, obwohl ein Socket, der Verbindungen unter Verwendung AcceptAsync asynchron akzeptiert, letztlich nur 1 neue Verbindung gleichzeitig verarbeiten würde, selbst wenn 1000 Clients gleichzeitig verbunden sind und abhängig davon, wie schnell Clients weitergeleitet werden könnten zu einem anderen Thread für die Verarbeitung, kann die Leistung begrenzen.

Das brachte mich dann dazu, mich zu fragen, ob ich meine Klasse überhaupt schreiben könnte, indem ich eine einzige Instanz von SocketAsyncEventArgs für alle Verbindungen anstelle eines Objektpools, den ich ursprünglich vorgesehen hatte.

Wenn ich mehr darüber nachdenke, frage ich mich, was passiert, wenn mehrere AcceptAsync-Anrufe getätigt werden, während keine Verbindungen verfügbar sind. Also habe ich diese sehr sehr grobe Testanwendung, um zu sehen, was passieren würde:

class Program 
{ 
    private static Socket s; 

    static void Main(string[] args) 
    { 
     s = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
     s.Bind(new IPEndPoint(IPAddress.Any, 10001)); 
     s.Listen(10); 
     var e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     while (true) 
     { 

     } 
    } 

    private static void EOnCompleted(object sender, SocketAsyncEventArgs socketAsyncEventArgs) 
    { 
     Console.WriteLine(socketAsyncEventArgs.AcceptSocket.RemoteEndPoint); 
     Thread.Sleep(10000); 
     var e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
    } 
} 

ich eine tcp Testanwendung verwendet zeigte die Anwendung mit Verbindungen und das Ergebnis spammen, dass die mehrere AcceptAsync Anrufe erstellt mehrere komplette Zyklen annehmen/dass schien parallel zu laufen.

Meine Frage ist, hat das tatsächlich irgendeinen Nutzen? Ist es eine legitime Möglichkeit, eine schnellere Verarbeitung eingehender Verbindungen zu ermöglichen? Gibt es irgendwelche Nachteile?

EDIT 1: Wenn man sich das genauer ansieht, scheint es technisch möglich zu sein, dass, wenn Daten für asynchrone Aufrufe immer verfügbar sind, sie alle synchron laufen und nie wieder eine neue Verbindung akzeptieren, oder zumindest eine erhebliche Verzögerung sein.

Wie können Sie einen Hochleistungsserver herstellen, wenn dies tatsächlich der Fall ist?

Antwort

1

Normalerweise ist alles, was benötigt wird, ist dieser Code:

while (true) { 
var socket = listener.Accept(); 
Task.Run(async() => await RunConnectionAsync(socket)); 
} 

Der Benutzer-Modus-Teil dieses Codes sehr schnell ausgeführt wird. Fast die gesamte Zeit wird im Kernel verbracht. Dies wird Verbindungen sehr schnell akzeptieren. Aufgrund des vom Kernel verwalteten Backlogs werden keine Verbindungen gelöscht.

Es gibt selten eine Notwendigkeit, mehrere ausstehende Annahmerufe zu haben, um Ihre Perf Ziele zu erreichen. Wenn Sie das benötigen, führen Sie einfach denselben Code für mehrere Threads aus.

Die API SocketAsyncEventArgs grenzt daran, veraltet zu sein. Taskbasiertes IO ist viel bequemer und nur ein wenig mehr CPU-intensiv.

Insbesondere, wenn Sie eine neue SocketAsyncEventArgs für jede Operation erstellen, verlieren Sie alle Vorteile dieses Musters. Das ist sinnlos.

Sie benötigen in fast allen Fällen kein asynchrones IO zum Akzeptieren von Sockets. Es kostet mehr CPU-Zeit zur Ausführung und hat daher etwas mehr Latenzzeit.

+0

Danke, das ist sehr hilfreich, das Internet ist überflutet mit Antworten und 10 Seiten Posts darüber, wie fast jeder andere Weg es ist der beste Weg. Meine einzige Sorge ist, ob es geeignet ist, "Task" für lang andauernde Aufgaben zu verwenden.Mein Server wird Tausende von Verbindungen für längere Zeit geöffnet haben. Wenn ich vollständig synchronen Zugriff auf jeden Socket verwenden würde, würde das bedeuten, dass ich tausende von Aufgaben die ganze Zeit laufen lassen würde. Ist das akzeptabel? – Ashigore

+0

Dies ist ein großartiger Fall für Async Socket IO mit warten. Sie können Tausende von Verbindungen auf diese Weise haben (keine Threads konsumierend). Die Annahme-Schleife ist jedoch nur 1 Thread. Niemanden interessierts. Machen Sie das, was Ihnen am ehesten passt, was normalerweise Sync IO ist. – usr

+0

Implementieren Sie daher RunConnectionAsync mit async IO und warten Sie ab. Die Annahmeschleife ist korrekt, wie sie in dieser Antwort steht. – usr

Verwandte Themen