Forum: PC-Programmierung send() und locale sockets


von Marco H. (damarco)


Lesenswert?

Hmm ich habe ein kleines Problem mit send() und lokalen Unix 
socket(SOCK_STREAM) als IPC zwischen einigen threads.

Offenbar wird nach dem send() der buffer Inhalt nicht sofort oder gar 
nicht in den Kernel Buffer geschrieben, so das nur noch Käse ankommt 
wenn man send() zu schnell hintereinander im selben Thread aufruft. 
Normalerweise blockiert es ja wenn der Inhalt nicht in den Buffer passt, 
es kopiert die Daten in den Kernel buffer und gibt die geschrieben bytes 
zurück.

Das kopieren in das socketFile erfolgt anscheint nicht sofort durch den 
Kernel wenn man das so nennen darf? Schickt man den thread dazwischen 
schlafen funktioniert es....

Keine Lösung, ich kann nicht wissen wann der Kernel fertig ist. select() 
hilft auch nicht da der descriptor wieder zum schreiben bereit ist, 
rauscht das ganze genau so wieder durch.

Ich müsste mein Result komplett zwischen buffern und dann senden. Ich 
wollte es in kleinen teilen senden.

Beim Server werden die Client Sockets gemultiplext per select(). Das 
wird doch nicht die Ursache sein ?

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Ohne code kann man nichts sagen. Du hast sicher irgendwo eine race 
condition oder sonst einen fehler. Behandelst du den EINTR fehler fall? 
Das Problem liegt aber definitiv nicht bei send oder dem kernel (ausser 
vielleicht falls du das WSL verwendest).

von foobar (Gast)


Lesenswert?

Hast du evtl den socket auf non-blocking gesetzt? Was sagt der 
Return-Wert von send?

von Marco H. (damarco)


Lesenswert?

Der Return Wert sind die gesendeten Bytes, kein Error. Das Problem ist 
ja nicht das blockieren, das geschieht ja nur wenn der Kernel Buffer 
voll ist. Sondern das der Inhalt des Datenbuffers zwischen den send() 
aufrufen verändert wird. Ich ging aber davon aus das die Daten schon vom 
Kernel geschrieben wurden, sobald send() zurück kehrt.

Send() ist nicht thread sicher, hmm ich schau nach.

Der Socket,Buffer usw. wird nur von einem thread verwendet...

: Bearbeitet durch User
von DPA (Gast)


Lesenswert?

Du könntest noch schauen, ob du mit "strace -f" etwas siehst. Bei 
Raceconditions kann es aber vorkommen, das der Fehler, den man Debuggen 
will, durch das geänderte Timing verschwindet.

von Jim M. (turboj)


Lesenswert?

Marco H. schrieb:
> müsste mein Result komplett zwischen buffern und dann senden. Ich
> wollte es in kleinen teilen senden.

Zeige uns den Code!

Ich kann mir sehr gut vorstellen das Du eine Race Condition in Deinem 
Code übersehen hast. Linux Kernel Zeuchs ist relativ gut ausgetestet.

von foobar (Gast)


Lesenswert?

Irgendwie hab ich den Eindruck, das du nicht send(2), den direkten 
Kernelcall, sondern irgendeine buffered Variante benutzt.

von Marco H. (damarco)


Lesenswert?

Den Eindruck habe ich auch... Ich bin auch der Meinung das es an meinen 
Code liegt ;). Ich habe er einen anderen verdacht... das hängt mit 
select() zusammen. Dann wenn die Situation auftritt landen daten 
master_socket, accept  läuft durch und gibt einen weiteren fd zurück...

Da ist was faul.... das passiert eben immer dann wenn das Problem 
auftritt, außer wenn man zwischen den senden den thread kurz schlafen 
legt.

von Marco H. (damarco)


Lesenswert?

also wenn man es in 1k blöcken sendet funktioniert es... sobald man mehr 
sendet läuft etwas über und es kommt zu Fehlfunktionen. Nun gut sende 
ich eben alles in 1k blöcken. Aber ich dachte das wäre beim localen 
socket egal, send blockiert komischer weise auch nicht..

von Marco H. (damarco)


Angehängte Dateien:

Lesenswert?

tada... jetzt funktioniert es. Die Ursache lag am fastcgi Modul, genauer 
gesagt in der read Funktion. Die Berechnung wie viele values übertragen 
werden war fehlerhaft :(.

Wenn man die Pakete nicht ausließt blockiert send() wie es soll, bis 
eben wieder etwas ausgelesen wird.

Zum Projekt....  Es wird ein AVB Controller der über HTTP/Rest.. Was man 
sieht ist die Antwort als Json und stellt die Geräte mit ihrer ID dar. 
3000 Geräte gibt es natürlich nicht ;) durch die MAC Adressen durch das 
MAAP ist das auf 256 Geräte begrenzt.  Der token dient später dazu auch 
kleineren Geräten mit begrenzten Buffer den controller zu nutzen. Per 
query übergibt man einfach maxResults und bekommt einen token zurück.

Der AVDECC Stack kann schon einiges ;), ich muss jetzt aber erstmal die 
Rest API hinbekommen um weiteres zu implementieren zu können.

Ich habe jetzt 30 fastCGI child Prozesse offen in dem auch das Json 
gebaut wird. Ist es besser Connect -> Daten Transfer -> Close ? oder 
offen lassen. Wenn ich den Socket immer wieder schließe bekomme ich im 
fastcgi Modul mit wenn der Server nicht läuft. Das Modul schläft ja und 
versucht dann was zu senden. Durch das load Balance macht das dann jedes 
der 30 childs und scheitert zuerst einmal.

Beitrag #5460603 wurde vom Autor gelöscht.
von Sheeva P. (sheevaplug)


Lesenswert?

DPA schrieb:
> Du könntest noch schauen, ob du mit "strace -f" etwas siehst. Bei
> Raceconditions kann es aber vorkommen, das der Fehler, den man Debuggen
> will, durch das geänderte Timing verschwindet.

strace(1) und ltrace(1) haben leider dasselbe Timing-Problem.

von Sheeva P. (sheevaplug)


Lesenswert?

Marco H. schrieb:
> Den Eindruck habe ich auch... Ich bin auch der Meinung das es an meinen
> Code liegt ;). Ich habe er einen anderen verdacht... das hängt mit
> select() zusammen. Dann wenn die Situation auftritt landen daten
> master_socket, accept  läuft durch und gibt einen weiteren fd zurück...

select(2), poll(2) und epoll(7) werden vielfach benutzt und sind daher 
sehr gut ausgetestet. Es wäre sehr ungewöhnlich, wenn Du ausgerechnet 
dort einem Fehler aufgesessen wärest.

> Da ist was faul.... das passiert eben immer dann wenn das Problem
> auftritt, außer wenn man zwischen den senden den thread kurz schlafen
> legt.

Dann mach' doch bitte mal ein Minimalbeispiel, das den Code 
reproduziert, und poste das hier. n Augen sehen ja meistens mehr als 
zwei.

von Marco H. (damarco)


Lesenswert?

Es funktioniert, der Read im Client war nicht gut gelöst. Das Problem 
ist das ich beim erzeugen des Json nachladen muss. Das Paket wird an 
mehreren Stellen je nach Buffer gebrochen und muss dann wieder zusammen 
geklebt werden.

Da kann schon mal was in die Hose gehen... Ich hatte mehrere Fehler 
drinnen.

1. Das Pakete im Socket Buffer blieben.
2. Das diese falsch zusammen geklebt wurden.

von Marco H. (damarco)


Angehängte Dateien:

Lesenswert?

So ich habe das mal extrahiert... Ich baue die Verbindung erst auf nach 
einen http Request auf und wenn keine traffic über das Modul läuft wird 
der socket geschlossen per messages "CLOSE" an den Server. Beim starten 
wird jetzt nicht mehr sofort eine Verbindung auf gebaut. Das umgeht das 
Problem wenn sich der Controller beendet und das fastCGI Modul davon 
nichts mitbekommt da es ja schläft. Erst beim Request merkt das 
betroffen Modul das die Verbindung fehlt. Das hatte den blöden 
Randeffekt das 30 Module erst mal an getriggert werden mussten. Per 
Zufall welches das Load balancing gerade ansprach. Daten -> Fehler -> 
Fehler -> Daten

Auf der Server Seite werde ich was ähnliches bauen, die letzte Aktivität 
überwachen und wenn auf dem socket nach x nichts los war muss dieser 
korrupt sein und er wird geschlossen. Das passiert nur wenn sich das 
fastcgi Modul bzw. der Webserver unschön beendet. Das Signal SIGTERM 
fange ich ja ab und schließe den Socket ordentlich.

Das lesen ist übrigens wichtig beim Einsatz von select, dadurch wird der 
FD zurückgesetzt. Ohne lesen des Sockets entsteht eine schleife...

Die Funktion wird in einer schleife aus geführt...

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.