Paralleler Server
-
Hallo,
ich habe ein Problem mit meinem Programm. Und zwar gibt es einen Server Parent Prozess, welcher per accept() auf Client Verbindungen wartet.
Verbindet sich ein Client, dann wird ein Server Child Prozess gestartet, jeweils für jeden Client ein neuer Prozess.
Der neue child Prozess wartet dann per accept() auf einem anderen Port auf Anfragen seines Clients.
Das Problem ist jetzt, dass mein Child Prozess keine Anfrage vom Client empfängt, obwohl dieser per send() an den neuen Port sendet, der Host müsste ja der selbe wie beim Server Parent sein, oder?
Kann mir jemand sagen, ob das vom Konzept her überhaupt richtig ist, wie ich das mache? Falls nein, wie macht man es dann oder wie kann man es noch machen?
-
Macht der neue Client zuvor ein connect() zu dem neuen Port? Sonst nützt send() wenig.
Das Konstrukt verstehe ich noch nicht so richtig. Ich gehe man davon aus, dass Du einen zentralen Server haben willst, der eigenständige Child's mit Socket-Verbindungen zu Clients aufbauen soll.
Das geht ganz grob so (so laufen die meisten Server, wie z.B. Web-Server, Internet-Daemon inetd):
Der zentrale parent-Server macht socket(), bind(), listen() und accept() auf einem speziellen Port, oder ggf. auch auf mehreren Ports (dann muss man noch ein select() vor dem accept() vorschalten).
Ein Client macht socket(), gethostbyname() und connect().
Der Server erhält mittels des accept() einen einen neuen bidirectionalen fd.
Jetzt würde ich einen fork() machen. Der parent-Server macht danach den neuen fd zu, der child-Prozess kann jetzt den neuen fd zur Kommunikation benutzen.
Vielleicht habe ich jetzt das alles beschrieben, was Du sowieso weißt. Dann vergiss es einfach. Aber das Konstrukt bzw. die fachlichen/technischen Anforderung würde mich doch grob interessieren, zumindest bei der Fragestellung "ob man es so richtig macht"
-
der child-Prozess kann jetzt den neuen fd zur Kommunikation benutzen.
das Problem is, ich weiß nicht, wie mein Programm server child, welches den Kindprozess mit execl() überlagert den Client Socket nutzen kann.
Momentan übergeben ich die Client Socket ID aus dem EP, wo ich sie ja per accept() bekommen hab, per execl() an das überlagernde server child Programm.
Aber das Child Programm erkennt scheinbar nicht den Socket, wahrscheinlich mach ich das falsch mit der Übergabe.
Achja, dazu kommt noch, dass der Child Prozess auf einem anderen Port horchen soll. Diesen übergebe ich auch per execl().
Den neuen Port sende ich aus dem Server Prozess über die bestehende client socket connection an den Client. Dort kommt sie auch an und mit dem neuen Port baue ich im Client einen neuen Socket zum neuen Port des Server Child auf.Ich hoffe das ist so richtig. Kommt imr etwas durcheinander vor. Ich habe ja jetzt:
Client - Server socket verbindung
fork() in Server
neuer port von server child für socket verbindung mit client an client senden
server child wartet auf socket verbindung auf neuem Port
client baut socket verbindung auf mit neuem Port, also zu server child
client sendet über diesen socket...aber es kommt nix an. Ich schaffs nicht den client mit dem überlagernden server child Programm auf einen neuen Port zu verbinden.
Wenn ich einen neuen Port verwende für die Kindprozess - Client Verbindung, dann ist doch eigentlich der alte Socket Descriptor aus dem Server Prozess (client = accept() ) nicht mehr der richtige, oder?
edit:
hier ist der superserver inetd beschrieben in 7 Schritten
http://www.virtualuniversity.ch/telekom/server/11.htmldas habe ich alles auch so bis auf Schritt 6, das mit dem dup2() habe ich nicht. Was genau bringt mir das kopieren des Deskriptors? Ich überlagere ja das Programm, wie bekommt das überlagernde Programm den Deskriptor?
Und noch ne Frage:
Es können sich ja beliebig viele clients mit meinem Server verbinden, und jeder client soll eigentlich nur noch mit dem passenden Server child kommunizieren, also jeder client hat einen server child.
Das mache ich momentan so, dass für jede Server Child - Client Verbindung ein neuer Port belegt wird, wo der Server child horcht, ob der client ne anfrage schickt.Ist das so ok? Da belege ich ja evtl. hunderte Ports. Kann das sein? Oder reicht mir im Grunde die socket id aus dem server Prozess, die ich per accept() bekommen habe, als sich der client verbunden hat?
Aber da kommen wir zurück auf mein erstes Problem, wie bekommt mein überlagerndes Programm diese socket id?Sorry, ich schreib soviel, weil ich keine Ahnung habe, wie ich das problem auf den Punkt bringen kann.
Im Grunde brauche ich sowas:
Server horcht auf Port 1111
Client verbindet sich auf Port 1111 mit server
Server erzeugt Kindprozess
Kindporzess wird mit execl() mit server child Programm überlagert (Deskriptoren gehen dabei ja verloren, dazu braucht man dann wohl dup2() )
Server bestimmt noch neuen Port 1112, worüber dieses ServerChild - Client Paar kommunizieren soll
Server schickt neuen Port an Client.
Jetzt wartet Server wieder auf neuen Client (accept)
ServerChild soll auf dem Port 1112 auf Verbindung warten (accept). (damit hat sich doch eigentlich der socket Desktiptor aus dem EP erledigt, oder?)
Client verbindet sich neu mit ServerChild auf Port 1112 und sendet etwas.Und die neue Verbindung klappt irgendwie bei mir nicht.
-
ich habe das Problem mittlerweile gefunden. Ich hatte im Client für das Senden zum ServerChild die Socket ID für den Server und den Port für ServerChild benutzt. Da kann natürlich nix ankommen beim ServerChild.
-
das Problem is, ich weiß nicht, wie mein Programm server child, welches den Kindprozess mit execl() überlagert den Client Socket nutzen kann.
Schritt (6) aus der Beschreibung zum inetd:
im neuen Child-Prozess nach fork():
close(1); /* close stdout */ fdout = dup(sockfd); /* stdout/fdout wird jetzt die Verbindung zum Socket WR */ close(0); /* close stdin */ fdin = dup(sockfd); /* stdin/fdin wird jetzt die Verbindung zum Socket RD */
Nach dem exec() kann man mit Ausgabe auf stdout (fdout==1) auf den socket schreiben, und von stdin (fdin==0) kann man lesen!
So in etwa macht es der inetd. Und funktioniert wunderbar auch woanders.
Wie es allerdings mit anderen Filedescriptoren funktioniert, könnte etwas problematischer werden. Ggf. alle anderen Filedescriptoren im Child zumachen (bis auf den sockfd) und dann die Filedescriptoren 3 und 4 nehmen. "Im Prinzip" werden die Filedescriptoren unverändert auch beim exec..() an das Child übergeben, es sei denn man benutzt FD_CLOEXEC (s.u.).Das von Dir beschriebene Konzept ist zumindest für "normale" Server-Client-Kommunikation etwas unüblich. Normalerweise benutzt man eine Portnummer, um einen bestimmten Dienst zu definieren.
weitere Tipps:
Vorm exec() mittels fcntl() das Bit FD_CLOEXEC setzen für alle fd's die der child-Prozess nicht benötigt => befreit von unnötig belegten Filedescriptoren.Reicht das erst einmal als Idee zur Verhinderung von unendlicher Anzahl der Ports? Sonst bitte weitere Fragen....