2016-04-14 15 views
1

Ich muss einen TCP-Server mit C# .NET 4.5 erstellen, muss es in der Lage sein, komfortabel mindestens 3.000 verbundenen Clients, die Nachrichten senden werden alle zu behandeln 10 Sekunden und mit einer Nachrichtengröße von 250 bis 500 Bytes.TCP-Server mit C# .NET 4.5, die auf Tausende von verbundenen Clients skalieren kann

Die Daten werden zur Stapelverarbeitung und Protokollierung in einen anderen Prozess oder eine Warteschlange ausgelagert.

Ich muss auch in der Lage sein, einen vorhandenen Client zu wählen, um Nachrichten (mehr als 500 Bytes) Nachrichten innerhalb einer Windows Forms-Anwendung zu senden und zu empfangen.

Ich habe noch nie eine Anwendung wie diese erstellt, daher basiert mein Wissen auf den verschiedenen Fragen, Beispielen und Dokumentationen, die ich online gefunden habe.

Mein Fazit ist:

  1. non-blocking async ist der Weg zu gehen. Vermeiden Sie es, mehrere Threads zu erstellen und IO zu blockieren.
  2. SocketAsyncEventArgs - Ist komplex und wird wirklich nur für sehr große Systeme benötigt, BTW was macht ein sehr großes System aus? :-)
  3. BeginXXX-Methoden reichen aus (EAP).
  4. Mit TAP kann ich 3. mithilfe von Task.Factory.FromAsync vereinfachen, aber es erzeugt nur das gleiche Ergebnis.
  5. Verwenden Sie eine globale Sammlung Spur der angeschlossenen TCP-Clients über

Was ich bin nicht sicher zu halten:

  1. Soll ich einen Manual verwenden, wenn sie mit der TCP-Client-Sammlung interagieren? Ich nehme an, dass die Asyc-Ereignisse den Zugriff auf diese Sammlung sperren müssen.
  2. Die beste Möglichkeit, einen getrennten Client zu erkennen nach Ich habe BeginReceive aufgerufen. Ich habe festgestellt, dass der Anruf feststeht und auf eine Antwort wartet, damit diese bereinigt werden muss.
  3. Senden von Nachrichten an einen bestimmten TCP-Client. Ich denke, Funktion in benutzerdefinierten TCP-Sitzungsklasse, um eine Nachricht zu senden. Muss ich in einem asynchronen Modell einen zeitgesteuerten Prozess erstellen, der eine Nachrichtenwarteschlange prüft, oder würde ich ein Ereignis in einer TCP-Sitzungsklasse erstellen, die Zugriff auf den TcpClient und den zugehörigen Stream hat? Wirklich interessiert an Meinungen hier.
  4. Ich möchte einen Thread für den gesamten Dienst nutzen und nicht blockierende Prinzipien verwenden innerhalb gibt es anythings sollte ich von espcially in Zusammenhang mit der 1. Manual etc ..
  5. Danke achtsam sein

für lesen. Ich bin sehr daran interessiert, konstruktive Gedanken und/oder Links zu Best Practices/Beispielen zu hören. Es ist eine Weile her, seit ich in C# so Entschuldigungen programmiert habe, wenn einige meiner Fragen offensichtlich sind. Aufgaben, async/warte sind neu für mich!

:-)
+0

Suchen Sie im Internet nach "IO Completion Ports .NET" – selbie

+0

@selbie Entschuldigung, da ich vielleicht nicht klar gewesen bin, möchte ich dies in C# tun. Haben Sie vorgeschlagen, dass ich C++ oder die in .NET implementierten IOCP-Konzepte verwende, dachte ich, dass es in .NET 4.5 einfachere und leistungsfähigere Alternativen geben würde. Es gibt ein Codeprojektbeispiel, das dies tut, aber ich dachte wieder, dass der Event/Task-gesteuerte Ansatz ausreichen würde. – Nicholas

+1

Alle asynchronen Socket-APIs verwenden bereits einen IOCP. –

Antwort

4

Ich brauche einen TCP-Server unter Verwendung von C# .NET 4.5+

Nun zu bauen, das erste, was zu bestimmen ist, ob es basen Knochen TCP/IP sein muss. Wenn Sie möglicherweise können, schreiben Sie eine, die eine höhere Abstraktion verwendet, wie SignalR oder WebAPI. Wenn Sie einen mit WebSockets (SignalR) schreiben können, tun Sie das und schauen Sie niemals zurück.


Ihre Schlussfolgerungen klingen ziemlich gut. Nur ein paar Anmerkungen:

SocketAsyncEventArgs - Ist komplex und wirklich nur für sehr große Systeme benötigt, BTW was ein sehr großes System ausmacht? :-)

Es ist nicht so sehr ein "großes" System in Bezug auf die Anzahl der Verbindungen. Es ist eher eine Frage, wie viel Verkehr im System ist - die Anzahl der Lese-/Schreibvorgänge pro Sekunde.

Die einzige Sache, die SocketAsyncEventArgs tut, ist, Ihre E/A-Strukturen wiederverwendbar zu machen. Die/End*() APIs erstellen eine neue IAsyncResult für jede E/A-Operation, und dies kann Druck auf den Garbage Collector verursachen. SocketAsyncEventArgs ist im Wesentlichen das gleiche wie IAsyncResult, nur es ist wiederverwendbar. Beachten Sie, dass es einige Beispiele gibt, die die SocketAsyncEventArgs APIs ohne verwenden, die die SocketAsyncEventArgs Strukturen wiederverwenden, die völlig lächerlich ist.

Und es gibt keine Richtlinien hier: schwerere Hardware wird die APM-APIs für viel mehr Verkehr verwenden können. In der Regel sollten Sie einen Barebone-APM-Server erstellen und zuerst einen Test durchführen und erst dann zu SAEA wechseln, wenn er auf der Hardware des Zielservers nicht funktioniert.

Auf die Fragen:

Sollte verwende ich eine Manual wenn sie mit der TCP-Client-Sammlung interagieren? Ich nehme an, dass die Asyc-Ereignisse den Zugriff auf diese Sammlung sperren müssen.

Wenn Sie TAP -basierte Wrapper verwenden, wieder await wird dann auf einem erfassten Kontext standardmäßig. Ich erkläre das in my blog post on async/await.

Es gibt ein paar Ansätze, die Sie hier nehmen können. Ich habe erfolgreich einen zuverlässigen und performanten single-threaded TCP/IP-Server geschrieben; das Äquivalent für modernen Code wäre, etwas wie my AsyncContextThread class zu verwenden. Es stellt einen Kontext bereit, der dazu führt, dass await standardmäßig auf demselben Thread fortgesetzt wird.

Das Schöne an Single-Thread-Servern ist, dass es nur einen Thread gibt, so dass keine Synchronisation oder Koordination notwendig ist. Ich bin mir jedoch nicht sicher, wie gut ein Single-Thread-Server skalieren würde. Vielleicht möchten Sie es ausprobieren und sehen, wie viel Ladung es aufnehmen kann. Wenn Sie mehrere Threads benötigen, können Sie einfach async Methoden im Thread-Pool verwenden. await hat keinen erfassten Kontext und wird daher in einem Threadpool-Thread fortgesetzt. In diesem Fall müssten Sie den Zugriff auf alle gemeinsam genutzten Datenstrukturen einschließlich Ihrer TCP-Client-Sammlung koordinieren.

Beachten Sie, dass SignalR das alles für Sie übernimmt. :)

Die beste Möglichkeit, einen getrennten Client zu erkennen, nachdem ich BeginReceive aufgerufen habe. Ich habe festgestellt, dass der Anruf feststeht und auf eine Antwort wartet, damit diese bereinigt werden muss.

Dies ist die half-open problem, die ich ausführlich auf meinem Blog diskutiere. Der beste Weg (IMO), dies zu lösen, besteht darin, periodisch eine "Noop" -Freiheitsmeldung an jeden Client zu senden.

Wenn das Ändern des Protokolls nicht möglich ist, ist die nächstbeste Lösung, die Verbindung nach einem Timeout ohne Kommunikation zu schließen. So entscheiden sich HTTP- "persistent" -/"keep-alive" -Verbindungen zu schließen. Es gibt eine andere mögliche Lösung (Ändern der Keepalive-Paket-Einstellungen auf dem Socket), aber es ist nicht so einfach (erfordert p/Invoke) und hat andere Probleme (nicht immer von Routern, nicht von allen OS TCP/IP-Stacks, etc. unterstützt).

Oh, und SignalR wird dies für Sie behandeln. :)

Senden von Nachrichten an einen bestimmten TCP-Client. Ich denke, Funktion in benutzerdefinierten TCP-Sitzungsklasse, um eine Nachricht zu senden. Muss ich in einem asynchronen Modell einen zeitgesteuerten Prozess erstellen, der eine Nachrichtenwarteschlange prüft, oder würde ich ein Ereignis in einer TCP-Sitzungsklasse erstellen, die Zugriff auf den TcpClient und den zugehörigen Stream hat? Wirklich interessiert an Meinungen hier.

Wenn Ihr Server kann Nachrichten an einen Client (dh es ist nicht nur ein Request/Response-Protokoll, ein Teil des Servers kann ohne den Client jeden Client um Nachrichten senden ein Update anfordern), dann ja, Sie Sie benötigen eine ordnungsgemäße Warteschlange mit ausgehenden Anfragen, da Sie mehrere gleichzeitige Schreibvorgänge auf einem Socket nicht (zuverlässig) ausführen können. Ich möchte, dass der Verbraucher nicht timerbasiert ist. Es sind Async-kompatible Producer/Consumer-Warteschlangen verfügbar (wie BufferBlock<T> from TPL Dataflow und it's not that hard to write one, wenn Sie async-compatible locks and condition variables haben).

Oh, und SignalR wird dies für Sie behandeln. :)

Ich möchte einen Thread für den gesamten Dienst verwenden und nicht blockierende Prinzipien innerhalb gibt es anythings ich sollte bewusst sein espcially in Zusammenhang mit der 1. Manual etc ..

Wenn Ihr gesamter Dienst single-threaded ist, sollten Sie überhaupt keine Koordinationsprimitiven benötigen. Wenn Sie jedoch den Thread-Pool verwenden, anstatt ihn aus Gründen der Skalierbarkeit mit dem Hauptthread zu synchronisieren, müssen Sie ihn koordinieren. Ich habe eine coordination primitives library, die Sie nützlich finden können, da ihre Typen synchrone und asynchrone APIs haben. Dies ermöglicht beispielsweise, dass eine Methode an einer Sperre blockiert, während eine andere Methode eine Sperre asynchron blockieren kann.


Sie haben vielleicht ein wiederkehrendes Thema um SignalR bemerkt. Verwenden Sie es, wenn Sie können! Wenn Sie müssen schreiben Sie einen unverhofften TCP/IP-Server und kann nicht SignalR verwenden, dann nehmen Sie Ihre ursprüngliche Zeit schätzen und verdreifachen. Ernst. Dann kannst du mit meiner TCP/IP FAQ blog series den Pfad des schmerzhaften TCP beginnen.

+0

Vielen Dank, ich schätze die Zeit, die Sie gebraucht haben, um auf meine Frage zu antworten.Bitte entblößen Sie mich, während ich diese Informationen morgen verdaue und zu Ihnen zurückkomme. Ich werde auch in SignalR schauen, ich wusste nicht, ASP.NET wäre ein geeigneter Anwärter. Um zu verdeutlichen, muss ich Nachrichten an die Clients senden, die denselben Socket verwenden, sie stellen die Verbindung her, nicht den Server. – Nicholas

+0

Kennen Sie irgendwelche "TCP" Serverbeispiele, die auf SignalR basieren? Ich brauche Zugriff auf den rohen TCP-Socket, da die Clients nur über TCP kommunizieren können und daher meine Antwort auf ihre initiierende Verbindung/Socket zurückgeschrieben wird. – Nicholas

+0

SignalR verwendet das WebSocket-Protokoll, sodass es nicht mit Raw-TCP/IP-Clients verwendet werden kann. Die Clients müssten geändert werden, um SignalR (oder mindestens WebSockets) zu verwenden. –