senden und empfangen auf der gleichen Buchse aus verschiedenen threads funktioniert nicht
Habe ich gelesen, dass Sie sollten sicher sein, aus verschiedenen threads gleichzeitig, aber mein Programm hat sich etwas komisch Verhalten und ich weiß nicht, was falsch ist.
Habe ich threads gleichzeitig die Kommunikation mit einem client-socket
- ein senden an eine Steckdose
- eine tun und wählen Sie dann "recv" - Vorgang aus der gleichen Steckdose
So, ich bin immer noch senden, der client hat bereits die Daten erhalten und der socket geschlossen.
Zur gleichen Zeit, ich mache eine select-und recv auf, dass die Steckdose, die gibt 0 zurück (da ist es geschlossen), so dass ich in der Nähe dieser Buchse. Doch, die senden noch nicht wieder dabei ist...und da ich in der Nähe nennen, die auf diesem sockel den senden-Aufruf schlägt mit EBADF.
Ich weiß der client die Daten erhalten hat, korrekt, da ich die Ausgabe, nachdem ich in der Nähe der Steckdose und es ist richtig. Aber, auf mein Ende, mein senden-Aufruf noch einen Fehler (EBADF), so möchte ich, um es zu beheben, damit es nicht scheitern.
Funktioniert dies nicht immer. Es passiert vielleicht 40% der Zeit. Ich benutze nicht überall schlafen. Bin ich der angeblich die Pausen zwischen sendet oder recvs alles oder?
Hier einige code:
Senden:
while(true)
{
//keep sending until send returns 0
n = send(_sfd, bytesPtr, sentSize, 0);
if (n == 0)
{
break;
}
else if(n<0)
{
cerr << "ERROR: send returned an error "<<errno<< endl; //this case is triggered
return n;
}
sentSize -= n;
bytesPtr += n;
}
Empfang:
while(true)
{
memset(bufferPointer,0,sizeLeft);
n = recv(_sfd,bufferPointer,sizeLeft, 0);
if (debug) cerr << "Receiving..."<<sizeLeft<<endl;
if(n == 0)
{
cerr << "Connection closed"<<endl; //this case is triggered
return n;
}
else if (n < 0)
{
cerr << "ERROR reading from socket"<<endl;
return n;
}
bufferPointer += n;
sizeLeft -= n;
if(sizeLeft <= 0) break;
}
Auf dem client, ich benutze den gleichen code erhalten, den ich dann rufen Sie close() auf den socket.
Dann auf meiner Seite, bekomme ich 0 von der der Anruf empfangen und auch rufen Sie close() auf den socket
Dann meine schicken, schlägt fehl. Es ist immer noch nicht fertig?! Aber meine Kunden haben bereits die Daten!
- Bitte fügen Sie einige code. Möglicherweise gibt es Fehler Fremd zu dem, was Sie vermuten.
- Danke, ich werde hinzufügen von code
- Könnte geben Sie immer die Buchse a reference count.
- Was meinst du? Wie wirkt sich das halten einer Referenz den Fehler vermeiden? Ich dachte gleichzeitige senden und recvs sind ok?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich muss zugeben, ich bin überrascht, dass du dieses problem so oft wie Sie tun, aber es ist immer eine Möglichkeit, wenn Sie den Umgang mit threads. Wenn Sie anrufen
send()
werden Sie am Ende gehen in den kernel zum Anhängen der Daten an den socket-Puffer in es, und es ist daher sehr wahrscheinlich, dass es gibt einen Kontext wechseln, vielleicht zu einem anderen Prozess im system. Inzwischen ist der kernel hat wohl gepuffert und übermittelt das Paket ganz schnell. Ich vermute, du hast den Test auf einem lokalen Netzwerk, so dass das andere Ende erhält die Daten und schließt die Verbindung und sendet die entsprechende FIN Sie zurück zu Ihrem Ende sehr schnell. Das alles könnte passieren, während die sendende Maschine ist noch mit der Ausführung anderer threads oder Prozesse, weil die Latenz auf einem lokalen ethernet-Netzwerk ist so gering.Nun die FIN kommt - erhalten-thread hat sich nicht viel getan in letzter Zeit, da es schon auf eine Eingabe wartet. Viele scheduling-Systeme wird deshalb erhöhen Ihre Priorität schon ein bisschen, und es gibt eine gute chance, es wird Nächstes ausgeführt werden soll (Sie müssen nicht angeben, für welches Betriebssystem Sie verwenden, aber dies ist wahrscheinlich passieren, zumindest auf Linux, zum Beispiel). Dieser thread schließt den socket durch seine null Lesen. Irgendwann kurz danach ist der senden-thread wird wieder geweckt, aber vermutlich ist der kernel merkt, dass der socket geschlossen wird, bevor es wieder aus dem blockierten
send()
und zurückEBADF
.Nun, dies ist nur Spekulation, wie die genauen Ursache - unter anderem ist es stark hängt von der Plattform ab. Aber Sie können sehen, wie dies geschehen könnte.
Die einfachste Lösung ist wohl die Verwendung
poll()
im sende-thread als gut, aber warten auf den socket zu schreiben-ready-statt-Lesen-fertig. Natürlich müssen Sie auch warten bis alle gepufferten Daten zu senden - wie Sie das tun, hängt davon ab, welcher thread puffert die Daten. Diepoll()
rufen Sie lassen Sie erkennen, wenn die Verbindung geschlossen wurde, indem Sie Kennzeichnen es mitPOLLHUP
, denen Sie erkennen können, bevor Sie versuchen, Ihresend()
.Als eine Allgemeine Regel, die Sie nicht in der Nähe einer Steckdose, bis Sie sicher sind, dass die sende-Puffer wurde vollständig geleert - Sie können nur sicher sein, dass dieses, sobald der
send()
Aufruf zurückgegeben wurde und zeigt an, dass alle übrigen Daten ist verschwunden. Ich habe reagiert auf diese in der Vergangenheit von der überprüfung der sende-Puffer-wenn ich eine null Lesen, und wenn Sie nicht leer ist setze ich ein "schließen" - Markierung. In Ihrem Fall ist das sendende thread würde dann verwenden Sie diese als Tipp zu tun, die schließen, sobald alles geleert wird. Das ist wichtig, denn wenn die remote-Ende eine halb-schließen mitshutdown()
dann erhalten Sie eine null Lesen, auch wenn es vielleicht noch Lesen. Sie könnte sich nicht sorgen, etwa die Hälfte schließt, jedoch, in diesem Fall Ihre Strategie oben ist OK.Schließlich, ich persönlich würde vermeiden Sie den Aufwand für das senden und empfangen von threads und habe nur einen einzigen thread, das macht beides - das ist mehr oder weniger der Punkt der
select()
undpoll()
einem einzigen thread der Ausführung für den Umgang mit einer oder mehr filehandles, ohne sich Gedanken über die Durchführung einer operation, die Blöcke und hungert, die andere verbindungen.send()
undrecv()
sind nicht unbedingt blockierenden Operationen. Erstens, Sie nur blockieren, wenn Sie nicht in der Lage irgendwelche Fortschritte zu machen, sonst werden Sie wieder einen Teilerfolg - der Punkt derselect()
undpoll()
ist zu erkennen, wenn solch ein Erfolg möglich ist (d.h. Sie wird nicht blockiert). Zweitens, können Sie einen socket nicht-blockierend mit derO_NONBLOCK
option. Drittensselect()
undpoll()
arbeiten sehr gut mit einem single-socket - lassen Sie Sie warten, bis mehrere Ereignisse auf, wie viele Deskriptoren, wie Sie haben, sogar eine. Sie sind entworfen, um die Waage zu viele, aber das bedeutet nicht aufhören, Sie mit Sie mit einem.select()
oderpoll()
dann im Prinzip müssen Sie sich nichtO_NONBLOCK
. Vorausgesetzt, Sie sind vorsichtig, diese Aufrufe garantieren Sie, dass Sie können einen einzelnen Anruf ansend()
oderrecv()
als angemessen, um der Veranstaltung, die Sie bekommen und es wird nicht blockiert. Die meisten Leute verwenden immer nochO_NONBLOCK
im Falle, dass Sie einen kleinen Fehler irgendwo, oder ist es für die Effizienz des seins in der Lage, um eine Schleife übersend()
/recv()
zu ziehen alle Daten in einem Rutsch selbst mit einer festen Puffer-Größe. Aber das ist die Durchführung der Wahl.Das problem gefunden. Es ist mit meiner Schleife. Beachten Sie, dass es eine unendliche Schleife. Wenn ich nicht mehr Links schicken, meine sentSize 0 ist, aber ich werde immer noch Schleife, um zu versuchen zu senden. In dieser Zeit, ist der andere thread bereits geschlossen wurde, in diesem thread, also ist mein senden-Aufruf für 0 bytes gibt einen Fehler zurück.
Ich es behoben durch ändern der loop-Schleife unterbrochen, wenn sentSize ist 0 und das problem behoben!