Linux – ioctl mit FIONREAD immer 0

Lesezeit: 4 Minuten

Benutzer-Avatar
Tobi

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 FIONREADwarte einfach darauf.

/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
    sleep(1);

  • Hinweis: Das ist ein Spielzeug-Beispielcode, tun Sie das nicht im echten Code 🙂

    – Karoly Horvath

    8. August 2011 um 9:21 Uhr

  • @y_H Hey, ich gebe ihm nur die Waffe :-))

    – Cnicutar

    8. August 2011 um 9:23 Uhr


  • Funktioniert! Vielen Dank! Hat auch ohne Schleife funktioniert (gib ihm einfach die Zeit um die Daten zu bekommen ^^ ) Aber ich habe jetzt noch eine Frage…

    – Tobi

    8. August 2011 um 9:28 Uhr

  • @Toby Du musst wirklich wirklich reinschauen select(2).

    – Cnicutar

    8. August 2011 um 9:31 Uhr

  • Ich frage mich, warum niemand vorgeschlagen hat, poll() oder epoll() zu verwenden. Und danach versuchen, die Größe der zum Lesen verfügbaren Daten zu ermitteln.

    – Benutzer1415536

    3. November 2013 um 10:05 Uhr

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.

  • Die Race-Condition ist nicht unbedingt wahr: Wenn der Socket blockiert, kann das gesamte Handling bereits erledigt werden, lange bevor Daten eintreffen. Der recv-Aufruf wartet dann einfach, bis die Daten endlich ankommen…

    – Aconcagua

    5. Oktober 2015 um 9:00 Uhr

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.

1130850cookie-checkLinux – ioctl mit FIONREAD immer 0

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy