Probleme mit select() und nichtblockierendem Socket

Post Reply
Message
Author
ElBlues
Posts: 127
Joined: 25. Mar 2008 12:50
Contact:

Probleme mit select() und nichtblockierendem Socket

#1 Post by ElBlues »

Ich glaube, ich habe hier ein grundsätzliches Verständnisproblem:

Ich habe einen Server, bei dem ich mit select() auf eingehende Daten und neue Verbindungen checke. Das funktioniert prinzipiell auch ganz gut. Sockets von eingehenden Verbindungen werden immer auf non-blocking gesetzt, so dass ich kontrollieren kann, wie lange die Empfangsroutine dauert (da ich nicht unbegrenzt lange an größeren Datenblöcken herumladen will und dabei die anderen Clients vernachlässige).

Das führt jetzt allerdings zu folgendem Effekt, wenn ein Client nach dem Connect sehr schnell Daten sendet:

1. select() kommt zurück, weil sich ein Client verbindet -> dieser wird akzeptiert
2. der Socket dieses Clients wird auf non-blocking gesetzt
3. es wird versucht, Daten mit recv() zu empfangen -> es wird nichts empfangen, recv() kehrt sofort zurück
4. es wird erneut select() aufgerufen -> dieses blockiert jetzt bis in alle Ewigkeit

Scheinbar sind die Daten schon da, so dass select() nichts mehr melden kann, recv() konnte sie aber noch nicht abholen.

Was nun? Wie erfahre ich, ob irgend einer der Sockets noch nicht empfangene Daten hat, wenn ich select() dafür nicht verwenden kann?
http://www.kaufkauf.net - nie wieder Einkäufe vergessen!

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#2 Post by Janka »

Du schreibst von "wenn sich ein Client verbindet" und "Connect" andererseits benutzt du recv(). Welches Protokoll verwendest du überhaupt?

Aber ohne Beispielcode kann man dir eh nicht vernünftig helfen, weil man das Problem nicht nachvollzogen bekommt.
Bereite erst einmal ein simples, kompilierbares Beispiel (Server und Client) vor, das das unerwünschte Verhalten zeigt.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

ElBlues
Posts: 127
Joined: 25. Mar 2008 12:50
Contact:

#3 Post by ElBlues »

Der Ablauf ist eigentlich nicht kompliziert:

1. Der Server wartet mit select(), ob auf irgend einem Socket eine Leseoperation stattfindet, der Serversocket ist dabei teil des FD-Set, das von select() überwacht wird
2. Ein Client verbindet sich mit dem Server, select() kommt zurück und der Client wird mit accept() angenommen; des weiteren wird der Clientsocket auf nicht-blockierend gesetzt
3. Es wird sofort versucht, mit recv() von diesem Clientsocket Daten zu lesen -> das klappt nicht, der Fehlercode sagt aus, dass recv() an dieser Stelle eigentlich blockieren würde

-> irgend wann hier trudeln die Daten vom Client tatsächlich ein

4. Der Code kehrt zu select() zurückt, es werden jetzt die Sockets des Servers und des Clients überwacht, allerdings meldet select() jetzt nicht, dass Daten da wären (vermutlich, weil diese Daten schon in einem internen Puffer sind?)

Wenn ich im Schirtt 4. das select() unterdrücke und einfach wiederholt recv() für diesen Client aufrufe, dann erhalte ich meine Daten.
http://www.kaufkauf.net - nie wieder Einkäufe vergessen!

User avatar
hjb
Pro-Linux
Posts: 3264
Joined: 15. Aug 1999 16:59
Location: Bruchsal
Contact:

#4 Post by hjb »

Hi!

EWOULDBLOCK besagt, dass noch keine Daten da sind. Man muss eine Schleife darum basteln und aus Effizienzgründen auch mit select auf Daten warten. Es ist klar, dass unmittelbar nach dem accept noch keine Daten da sind. Jedenfalls nicht immer.

Grüße,
hjb
Pro-Linux - warum durch Fenster steigen, wenn es eine Tür gibt?

ElBlues
Posts: 127
Joined: 25. Mar 2008 12:50
Contact:

#5 Post by ElBlues »

Das ist doch aber gerade der Knackpunkt: Nach diesem EWOULDBLOCK muss ich mich darum kümmern, ob andere Clients was wollen bzw. ob sich neue Clients verbinden.

Also rufe ich select() auf - was aber blockiert, obwohl inzwischen Daten für diesen Socket da wären!
http://www.kaufkauf.net - nie wieder Einkäufe vergessen!

User avatar
hjb
Pro-Linux
Posts: 3264
Joined: 15. Aug 1999 16:59
Location: Bruchsal
Contact:

#6 Post by hjb »

Hi!

Wenn das select blockiert, sind eben noch keine Daten da. Oder du verwendest den falschen Dateideskriptor.

Wenn das Programm nichts weiter zu tun hat als alle Clients zu bedienen, dann musst du die Dateideskriptoren aller Clients in das select aufnehmen, und wenn select zurückkommt, prüfen, welche zu bearbeiten sind.

Grüße,
hjb
Pro-Linux - warum durch Fenster steigen, wenn es eine Tür gibt?

Post Reply