Clientsockets aus der vhlib
-
netzwerkprogger schrieb:
Hi!
Soll die Library jetzt eigentlich nur für dich (für den Webserver) sein oder für die Allgemeinheit?
Wenns für die Allgemeinheit ist sollte es wohl ein bisschen flexibler sein.ich hab mich lange mit iocompletion ports rumgeschlagen. und ich weiß auch, wie verflix schnell die sind. und recht einfach auch noch.
sei doch mal so freundlich und gib ein statement ab zu
http://www.cs.ualberta.ca/~paullu/C498/events.bad.idea.vonbehren.pdf
ignorieren wir zudem die velen messungen auf 2.4-er kernels, denn da skalierten threads noch schlecht.und nu?
ein thread pro client ist eine feine sache. ich werde keine async-sockets coden, nur weil jemand das haben wollen könnte. ja, ich gehe sogar so weit, daß die files kein seek und tell haben werden, sondern nur streams sind. für eigene projekte werde ich die vhlib nehmen. hab ich auch schon für kleinigkeiten und es fühlte sich gut an.
naja, außerdem muss ich extreme abfahren, um sie zu testen. falls ich unrecht haben sollte, und ein thread pro client keine feine sache ist, so muß das doch mal einer nachweisen. wie anders, als daß man es ausprobiert? und falls ein client pro thread eine feine sache ist, werde ich bald den lighttd plattmachen und das nehmen wir dann auch als hinweis an.
gestern abend hat jemand den neuen code linuxifiziert, der die vhlib vorher noch nie gesehen hat. hat mit labern und so nur ne stunde gedauert. das nehme ich als hinweis, daß ich die system-abhängigkeiten jetzt richtig gekapselt habe.
und es ist anzunehmen, daß ein paar leute die vhlib auch mögen werden. so zwischen 3 und 10 wohl. sie sollte aber ein interessanter lesestoff sein (notizmach: muss ein buch drüber schreiben).
außerdem ist sie zur not ja auch noch erweiterbar und du kannst dir asnyc-sockets dazuschreiben. werde ich aber nicht, weil Vector und Array und Stack dann wichtiger sind. und dann ein memory allocator und files und direkteres cout und vielleicht regexp, vielleicht filemapping, threads, wie beende ich alle threads, die im recv oder accept hängen so, daß sie ihre objekte destruieren? ist dazu auf linux das signal EINTR geeignet? stoppt das alle blockierenden calls und läßt sie einen fehler zurückgeben?
-
Zum Thema skalierbare Netzerke ist folgendes immer interessant: http://bulk.fefe.de/scalable-networking.pdf
Zu EINTR: EINTR ist ein Fehlercode und kein Signal. Wenn, während ein blockierender Systemaufruf wartet, ein Signal auftritt, dann kann der Systemaufruf unterbrochen werden. Der Rückgabewert ist -1 und errno wird auf EINTR gesetzt. Dann ist es meistens sicher den vorheringen Systemaufruf zu wiederholen. Ob, wann und welche Systemaufrufe von einem Signal unterbrochen werden ist vom System und einigen programmierbaren Einstellungen abhängig.
Zu Threads: Threads können durch pthread_cancel an einigen Stellen unterbrochen werden. Generell würde ich aber von pthread_cancel abraten. Bei IO ist die Lösung einfach. Es wird eine Pipe zwischen dem Masterthread und den Slaves errichtet. IO findet immer über select statt. Es wird auf eine Meldung durch die Pipe gewartet oder auf die gewünschten Daten. Falls sich die Pipe meldet, kann sich der Thread beenden. Falls sich die Daten melden, werden sie verarbeitet. In diesem Modell werden auf einfache Weise Raceconditions vermieden. Ebenso hat man weniger Aufräumprobleme als beim pthread_cancel.
Beispielcode, der diese Technik verwendet:
int maxfdp1 = (pipefd_[0] < server.socket()) ? server.socket() : pipefd_[0]; ++maxfdp1; fd_set rset; FD_ZERO(&rset); while(true) { FD_SET(pipefd_[0], &rset); FD_SET(server.socket(), &rset); log("ServerThread: Listening for incoming connections."); int ret = select(maxfdp1, &rset, 0, 0, 0); if (ret < 0) { if (errno == EINTR) continue; log(QString("ServerThread: Call to select failed with error %1.").arg(errno)); break; } if (FD_ISSET(pipefd_[0], &rset)) { log("ServerThread: Got request to terminate"); break; } if (FD_ISSET(server.socket(), &rset)) { int clientfd = server.accept(); if (clientfd < 0) { std::cout << "ServerThread: Accept on new connection failed." << std::endl; continue; } client_ = new QSocketDevice( clientfd, QSocketDevice::Stream); log(QString("ServerThread: New connection established with %1:%2.") .arg(client_->peerAddress().toString()).arg(client_->peerPort())); QApplication::postEvent(&topkate_, new QCustomEvent(Connect)); processClient(); QApplication::postEvent(&topkate_, new QCustomEvent(Disconnect)); log("ServerThread: Connection finished."); delete client_; client_ = 0; if (quitflag_ == true) break; } }
-
Ponto schrieb:
Zum Thema skalierbare Netzerke ist folgendes immer interessant: http://bulk.fefe.de/scalable-networking.pdf
hui.
hab's mir ausgedruckt und gaanz langsam durchgelesen.Ponto schrieb:
Bei IO ist die Lösung einfach. Es wird eine Pipe zwischen dem Masterthread und den Slaves errichtet. IO findet immer über select statt. Es wird auf eine Meldung durch die Pipe gewartet oder auf die gewünschten Daten. Falls sich die Pipe meldet, kann sich der Thread beenden. Falls sich die Daten melden, werden sie verarbeitet.
ok. das ist nicht gerade die lösung meiner träume, aber sie hat mich dazu gebracht, ganz viel zu überlegen.
(zwischendurch war ich wiedermal sicher, ich müßte IoComletionPorts verwenden.)
mit dem link oben noch kam ich auf das problem der timeouts. was ist, wenn ein client meinen server connected und nix sendet? wenn das viele machen udn ich nur syncrones read() nehme, bringen die ganz leicht meinen server um.
im moment stelle ich mir das so vor: es gibt *eine* globale pipe oder sowas, die zum kicken aller threads (genauer sockets) benutzt wird. ich lese mit blockierendem read(), aber immer nur, nachdem ein select (mit timeout!) mir das vorher erlaubt hat. ich mache select immer auf den zu lesenden socket und den globalen alle-sockets-killer. bei kill werfe ich ne exception.
-
so, empfangen einer html-seite geht wieder auf beiden systemen.
änderungen:
system-sachen sind nur noch in sysWindowsImpl.hpp und sysWindowsImplCpp.hpp bzw
sysLinuxImpl.hpp und sysLinuxImplCpp.hpp.in destruktoren, die eigrntlich keinen fehler werfen dürfen (wie welche, die
close() aufrufen, wobei ich im ctor schon gesichert habe, daß open() funktioniert
hat, machen nur noch assert statt SYSCHECK.es wird zuerst getaddrinfo aufgerufen und danch erst socket, damit
die infos aus getaddrinfo zur konstruktion des sockets verwendet werden können.
wenn ich richtig lese, bin ich so sogar alle entscheidungen ob ipv4 oder ipv6
los. ich gebe einfach keine hints an und der kumpel macht schon das beste draus.damit konnte auch die häßliche provate erblicheit bei den sockets
irgendwie verschwinden.und jetzt spiele ich erstmal wieder ein wenig mit primzahlen, um wieder normal im kopf zu werden.
-
volkard schrieb:
(zwischendurch war ich wiedermal sicher, ich müßte IoComletionPorts verwenden.)
mit dem link oben noch kam ich auf das problem der timeouts. was ist, wenn ein client meinen server connected und nix sendet? wenn das viele machen udn ich nur syncrones read() nehme, bringen die ganz leicht meinen server um.
im moment stelle ich mir das so vor: es gibt *eine* globale pipe oder sowas, die zum kicken aller threads (genauer sockets) benutzt wird. ich lese mit blockierendem read(), aber immer nur, nachdem ein select (mit timeout!) mir das vorher erlaubt hat. ich mache select immer auf den zu lesenden socket und den globalen alle-sockets-killer. bei kill werfe ich ne exception.
Das hört sich schon mal nicht schlecht an. Die Pipe kann man auch für andere Informationen verwenden. Blockierende Sockets sind eigentlich auch immer besser als nicht blockierende. Der einzige notwendige nicht blockierende Aufruf, der mir einfällt ist connect(), aber der Rest kann der Einfachheit habler blockierend sein.
Ein Problem von select ist das FD Array. Das skaliert nicht beliebig, aber es gibt ja poll und epoll.
-
Ponto schrieb:
Der einzige notwendige nicht blockierende Aufruf, der mir einfällt ist connect(),
warum?
-
volkard schrieb:
Ponto schrieb:
Der einzige notwendige nicht blockierende Aufruf, der mir einfällt ist connect(),
warum?
Ein Server der bestehende und neue Verbindungen per select bearbeitet kann in folgende Situation kommen:
1. Er wartet in select auf neue Verbindungen oder Daten in bestehenden Verbindungen.
2. Eine neue Verbindung wird gemeldet.
3. Der Server ruft connect um die Verbindung anzunehmen.
4. In der Zwischenzeit hat aber der Client die Verbindung bereits abgebrochen, so dass keine Verbindung mehr ansteht und der Server beginnt in connect zu blockieren, bis ein anderer Client sich verbindet.
5. Während der Server auf connect wartet, machen auch die anderen Verbindungen nichts.
-
meinst du accept statt connect?
-
volkard schrieb:
meinst du accept statt connect?
Ups. Ja, ich meine accept statt connect.
-
wenn ich richtig lese, bin ich so sogar alle entscheidungen ob ipv4 oder ipv6
los. ich gebe einfach keine hints an und der kumpel macht schon das beste draus.Aber SocketType (SOCK_STREAM / SOCK_DGRAM / ...) sollte man schon angeben können. Das kann man ja nicht dem Zufall überlassen...