Ich versuche herauszufinden, wie viele Bytes an meinem TCP-Socket lesbar sind. Ich rufe ioctl mit dem Flag “FIONREAD” auf, das mir eigentlich diesen Wert geben sollte. Wenn ich die Funktion aufrufe, bekomme ich als Rückgabewert 0 (also keinen Fehler), aber auch mein Integer-Argument bekommt den Wert 0. Das wäre kein Problem, aber wenn ich die Methode recv() aufrufe, lese ich tatsächlich einige Bytes aus dem Socket. Was mache ich falsch?
// hier etwas Code:
char recBuffer[BUFFERLENGTH] = {0};
int bytesAv = 0;
int bytesRead = 0;
int flags = 0;
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{
// Error
}
if ( bytesAv < 1 )
{
// No Data Available
}
bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
Wenn ich die recv-Funktion aufrufe, lese ich tatsächlich einige gültige Daten (was ich erwartet hatte).
Es geht sehr schnell, deshalb sieht man nichts. Was tust du:
ioctl
: Gibt es Daten für mich ? Nein, noch nichts
recv
: Sperren, bis Daten für mich vorhanden sind. Einige (kurze) Zeit später: Hier sind Ihre Daten
Also, wenn Sie wirklich sehen wollen FIONREAD
warte einfach darauf.
/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
sleep(1);
Die wirkliche Antwort hier ist, select(2) zu verwenden, wie cnicutar sagte. Toby, du verstehst nicht, dass du eine Race Condition hast. Zuerst schaut man sich den Socket an und fragt, wie viele Bytes da sind. Dann, während Ihr Code den Block „keine Daten hier“ verarbeitet, werden Bytes von der Hardware und dem Betriebssystem asynchron zu Ihrer Anwendung empfangen. Wenn also die Funktion recv() aufgerufen wird, ist die Antwort “keine Bytes verfügbar” nicht mehr wahr …
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{ // Error
}
// BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
if ( bytesAv < 1 ) // AND HERE!
{
// No Data Available
// BUT BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
}
// AND MORE BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
// AND NOW bytesRead IS NOT EQUAL TO 0!
Sicher, ein kleiner Schlaf hat Ihr Programm vor zwei Jahren wahrscheinlich repariert, aber es hat Ihnen auch eine schreckliche Programmierpraxis beigebracht, und Sie haben die Gelegenheit verpasst, zu lernen, wie man Sockets richtig verwendet, indem Sie select () verwenden.
Außerdem können Sie, wie Karoly Horvath sagte, recv anweisen, nicht mehr Bytes zu lesen, als Sie im Puffer speichern können, den der Benutzer übergeben hat. Dann wird Ihre Funktionsschnittstelle zu “Diese fn gibt so viele Bytes zurück, wie auf dem Socket verfügbar sind, aber nicht mehr als [buffer size you passed in]”.
Dies bedeutet, dass sich diese Funktion nicht mehr um das Löschen des Puffers kümmern muss. Der Aufrufer kann Ihre Funktion so oft wie nötig aufrufen, um alle Bytes daraus zu löschen (oder Sie können eine separate fn bereitstellen, die die Daten vollständig verwirft und diese Funktionalität nicht in einer bestimmten Datenerfassungsfunktion bindet). Ihre Funktion ist flexibler, indem Sie nicht zu viele Dinge tun. Sie können dann eine Wrapper-Funktion erstellen, die für Ihre Datenübertragungsanforderungen einer bestimmten Anwendung intelligent ist und die fn die get_data fn und die clear_socket fn nach Bedarf für diese bestimmte App aufruft. Jetzt bauen Sie eine Bibliothek auf, die Sie von Projekt zu Projekt und vielleicht von Job zu Job mitnehmen können, wenn Sie das Glück haben, einen Arbeitgeber zu haben, der Ihnen erlaubt, Code mitzunehmen.
Verwenden Sie select(), dann ioctl(FIONREAD) und dann recv()
Sie machen nichts falsch, wenn Sie blockierende E / A verwenden, wird recv () blockieren, bis die Daten verfügbar sind.