Wie Server programmieren für viele mögliche Clients?
-
omg benutz Asynchronous I/O
http://msdn.microsoft.com/msdnmag/issues/05/08/HighPerformanceSockets/default.aspx#S6
-
Auf CodeProject gibt's ein paar gute Artikel zu Netzwerken
Ist schon etwas her, dass ich die gelesen hab, aber ich glaube da werden auch massen-Verbindungen behandelt.Oh, sorry, das war aus dem MFC/C++ Teil
Naja, aber das Prinzip sollte da auch gut behandelt werden, ich hoffe nur dein Englisch ist gut
Und hier das könnte vielleicht noch interessant sein
..naja oder generell hier.. wirst schon was finden
-
Also. Ein paar Sachen.
Bei 20 Verbindungen gleichzeitig geht die "1 Thread pro Verbindung" Variante noch problemlos. Wir haben einen Server laufen der so programmiert ist, und der verträgt auch locker 100 Verbindungen pro Sekunde. Wo schluss ist kann ich dir nicht sagen, da vorher die Datenbank (MSSQL auf einem eigenen Server) schlapp macht -- die Abfragen sind z.T. recht rechenintensiv.Einerseits wird immer davon abgeraten, für einen Client einen eigenen Thread zu erzeugen, da dies die Systemresource merklich belastet.
Richtig so. Ein Thread braucht unter Windows normalerweise 1MB Stack, und das ist nicht wenig. Bei 32 Bit Systemen ist also spätestens bei 2000 Verbindungen tilt, da der Adressraum ausgeht (Speicher ist dabei meist kein Problem, da kaum ein Thread jemals auch nur annähernd 1MB Stack wirklich *verwendet* -- und committed wird der Speicher erst wenn er verwendet wird).
Weiters kann man beim 1 Thread/Connection Modell schlecht bzw. nur mit Umständen kontrollieren wieviele Threads wirklich gleichzeitig laufen. Laufen (zu) viele Threads gleichzeitig, dann passieren einige Dinge die bremsen. Genauso wenn ein Thread oft schlafen gelegt wird (z.B. durch einen blocking Call auf einen Socket) und danach bald wieder aufgeweckt, dann ist das genauso unnötiger Overhead. Der gleiche Thread könnte ja in der Zwischenzeit (ohne Context Switch!) andere Dinge erledigen, also z.B. Pakete von anderen Verbindungen bearbeiten.Asynchrone Sockets scheinen andererseits nur einen Scheinlösung zu sein, da im Hintergrund ein ständiger Poll stattfindet, der das System ausbremst. Leider haben sich alle Autoren dazu ausgeschwiegen, in welcher Größenordung das relevant ist.
Wie kommst du auf die Idee?
.NET verwendet bei asynchronen Sockets (z.B. BeginReceive) kein Polling, glaub's mir
Dafür pro Socket noch zusätzlich einen EVENT. Wie tragisch das ist kann ich dir leider nicht sagen, aber da MS das so gemacht hat wird es schon nicht die dümmste Lösung sein.Und selbst wenn wäre es nicht tragisch. Wenn du z.B. 10 Worker Threads hast, und alle pollen z.B. im 10ms Takt sämtliche Sockets -- das tut keinen weh solange sonst nix zu tun ist (wird kaum im Taskmanager sichtbar sein dass der Prozess überhaupt irgendwas tut). Und WENN mal so viel zu tun ist hast du schnell den Fall dass alle 10 Threads gleichzeitig Messages verarbeiten anstatt zu pollen, d.h. es geht genau dann wenns wichtig ist (nämlich bei Peaks) sowieso kaum Zeit durchs Polling drauf.
Ich wäre extrem dankbar, wenn mir jemand eine Größenvorstellung geben könnte, bei denen die oben genannten Lösungen versagen.
Das hängt aber SO extrem von der Maschine und der Konfiguration des OS (speziell TCP/IP Stack) ab. Pi mal Daumen mit heutiger "normaler" Hardware (2-4 Cores, ~4GB RAM) würde ich unter Windows bei 10k bis 100k TCP Verbindungen mit Problemen rechnen. Unter anderem liegt das daran dass "Windows selbst" (der TCP/IP Stack eben) Resourcen pro Verbindung verbrät -- z.B. Speicher für das Receive-Window.
Bei UDP sieht die Sache allerdings schonwieder ganz anders aus. 50k Verbindungen waren für Silent Bob damals (2003) z.B. kein Problem -- auf einem dual Pentium III (2x 1GHz) mit 1GB RAM, also nicht gerade die wahnsinns Hardware. Die heutigen Esel-Server schaffen AFAIK ca. das 10 fache.
-
Bei UDP gibt es keine Verbindungen. Aber da muss wie bei TCP bei 0xffff offenen Ports Schluss sein, weil es nicht mehr Ports gibt
-
-Foo- schrieb:
Bei UDP gibt es keine Verbindungen. Aber da muss wie bei TCP bei 0xffff offenen Ports Schluss sein, weil es nicht mehr Ports gibt
Falsch und falsch.
Bei UDP kannst du "Verbindungen" machen soviele du willst, da du dafür serverseitig keine Ports verbrauchst. Die "Verbindungen" musst du dir natürlich selbst irgendwie implementieren, das ist klar, ich dachte dass ich das voraussetzen könnte. Ebenso wie den Umstand dass UDP nicht reliable und nicht orderen etc. ist, man sich diesen Layer also selbst basteln muss.
Serverseitig verwendest du dann einen einzigen Port, und unterscheidest deine "Verbindungen" nach Source-IP und Source-Port.Bei TCP kannst du genauso mehr Verbindungen haben als es Ports am Server gibt, da ein Serverprogramm Verbindungen üblicherweise auf einem einzigen Port annimmt.
Eine Verbindung wird identifiziert über "Local-IP, Local-Port, Remote-IP, Remote-Port". Wenn dabei Local-IP und Local-Port für 100000 Verbindungen gleich ist ... na und? Ports werden dadurch auf jeden Fall keine verbraucht, also limitieren die 16 Bit für die Portnummer auch nicht die Anzahl der offenen Verbindungen.
Also wo soll jetzt das Problem sein?(BTW: ja, natürlich ist bei 2^16 offenen "Ports" schluss, nur du verwechselst Ports mit Verbindungen)
-
@hustbaer: gib doch einfach zu das du unrecht hattest und gut ist.
-
Es hat schon einen Grund warum UDP als verbindungsloses Protokoll gilt... Auf der Protokollebene (und nur die zählt wenn man zwei Protokolle miteinander vergleicht) hate UDP _keine_ Verbindungen. Alles was man mit dem Protokoll kann ist ein Paket an eine bestimmte Zieladresse losschicken und hoffen das es dort ankommt.
Auch die Sache mit den Verbindungen bei TCP ist so nur die halbe Wahrheit. Es stimmt insofern, daß es auf dem Server eine listening socket gibt die immer auf dem gleichen Port liegt. _Aber_ wenn eine Verbindung aufgebaut wird, legt der Server für jede Verbindung automatisch eine neue Socket an die einen eigenen Port zugewiesen bekommt ( http://en.wikipedia.org/wiki/Ephemeral_port , http://www.ncftp.com/ncftpd/doc/misc/ephemeral_ports.html ). Der Vorgang ist nachzulesen in Unix Network Programming 2nd edition (Richard Stevens) Seite 45. Die maximale Anzahl der Verbindung wird also durch die maximale Anzahl an möglichen Ports bestimmt, im Falle von BSD stehen dafür sogar nur Ports von 1024 bis 4999 zur Verfügung.
PS: Nur weil Du es nciht verstehst muß es nicht falsch sein...
-
Ephemeral Ports werden für ausgehende Verbindungen verwendet.
Ja, beim "accept" wird ein neuer *Socket* angelegt, aber kein neuer *Port*.Another important ramification of the ephemeral port range is that it limits the maximum number of connections from one machine to a specific service on a remote machine! The TCP/IP protocol uses the connection's 4-tuple to distinguish between connections, so if the ephemeral port range is only 4000 ports wide, that means that there can only be 4000 unique connections from a client machine to a remote service at one time.
Da steht genau das was ich beschrieben habe, nur etwas detailierter.
Die Ephemeral Port Range limitiert also die Anzahl der gleichzeitigen Verbindungen die ein Client gleichzeitig zu einem Server haben kann.
Guck dir doch bloss mal die TCP/IP Paketstruktur an, im "SYN-ACK" ist garkein Platz für die Angabe des neuen Ports, der Client könnte also garnicht wissen dass er mit dem Server jetzt auf einmal über einen anderen Port reden muss.Und, oh Leute, ich weiss schon wie UDP funktioniert. Dass UDP keine Verbindungen kennt ändert aber nix daran dass man über UDP Verbindungen herstellen kann, nämlich wenn man sich das fehlende selbst dazuprogrammiert.
Und das kann dann leicht wesentlich resourcenschonender sein, weil ein selbstgestricktes Serverprogramm besser als der TCP/IP Stack weiss wo welche und wie grosse Puffer gebraucht werden.Also. Nur weil ihr es nicht versteht heisst das nicht dass ich unrecht habe
-
Könnte man das vielleicht in die FAQ verschieben? In RudP oder so? Meine Lesezeichen quellen über
Aso, und wie sieht eigentlich das Fazit aus? Asynchrone Sockets? Oder x Threads und jeder übernimmt alle/x (<-geteilt durch) Verbindungen?
-
Auf jeden Fall asynchrones IO, am besten in Kombination mit einem Thread-Pool.
Ob der (Thread-Pool) ne fixe grösse hat oder dynamisch angepasst wird hängt von der Anwendung ab.