Probleme mit select() und nichtblockierendem Socket

Antworten
Nachricht
Autor
ElBlues
Beiträge: 127
Registriert: 25. Mär 2008 12:50
Kontaktdaten:

Probleme mit select() und nichtblockierendem Socket

#1 Beitrag von ElBlues » 06. Okt 2010 19:45

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!

Benutzeravatar
Janka
Beiträge: 3580
Registriert: 11. Feb 2006 19:10

#2 Beitrag von Janka » 06. Okt 2010 22:45

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
Beiträge: 127
Registriert: 25. Mär 2008 12:50
Kontaktdaten:

#3 Beitrag von ElBlues » 07. Okt 2010 7:19

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!

Benutzeravatar
hjb
Pro-Linux
Beiträge: 3245
Registriert: 15. Aug 1999 16:59
Wohnort: Bruchsal
Kontaktdaten:

#4 Beitrag von hjb » 07. Okt 2010 10:23

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
Beiträge: 127
Registriert: 25. Mär 2008 12:50
Kontaktdaten:

#5 Beitrag von ElBlues » 07. Okt 2010 17:03

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!

Benutzeravatar
hjb
Pro-Linux
Beiträge: 3245
Registriert: 15. Aug 1999 16:59
Wohnort: Bruchsal
Kontaktdaten:

#6 Beitrag von hjb » 08. Okt 2010 10:23

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?

Antworten