So überprüfen Sie die für einen Socket verfügbare Datenmenge in C und Linux

Lesezeit: 6 Minuten

Jimms Benutzeravatar
Jimm

Ich habe einen Server, der einen kontinuierlichen Datenstrom empfängt. Im Gegensatz zum mehrfachen Lesen aus einem Socket möchte ich die gesamten Daten im Socket-Empfangspuffer mit einem Systemaufruf lesen read().

Natürlich kann ich einen großen Puffer übergeben und read() wird versuchen, es mit allen verfügbaren Daten zu füllen. Dies würde jedoch viel Speicher verschwenden, da der mallocierte Puffer meistens größer wäre als die tatsächlichen Daten, die auf dem Socket verfügbar sind. Gibt es eine Möglichkeit, die verfügbaren Daten auf einer Steckdose abzufragen?

  • Es gibt wirklich keine gute Möglichkeit, dies zu tun – aufgrund der Nachrichtenfragmentierung auf der Seite des Absenders erhalten Sie möglicherweise nicht eine vollständige Nachricht auf Anwendungsebene auf einmal. Der richtige Weg, dies zu tun, ist select() und dann read() ein Stück von einiger Größe aus dem Socket, und sehen Sie dann, ob Sie eine vollständige Nachricht haben. Wenn dies der Fall ist, entfernen Sie diese Nachricht aus dem Puffer und verarbeiten Sie sie. Gehen Sie dann zurück zu select().

    – CDhowie

    27. Dezember 2012 um 0:07 Uhr

  • ich habe den begriff einer nachricht absichtlich nicht eingeführt. Meine Frage bezieht sich auf das einfache Lesen aller Daten, die zum Zeitpunkt des Read-like-Systemaufrufs verfügbar waren.

    – Jimm

    27. Dezember 2012 um 0:39 Uhr

  • Dann heißt es: „Rufen Sie weiter an read() bis um select() mit einem Timeout von Null (nicht NULL) gibt eine leere Read-Ready-Liste zurück.

    – CDhowie

    27. Dezember 2012 um 0:42 Uhr

  • Warum das ein Problem ist, ist mir unklar.

    – CDhowie

    27. Dezember 2012 um 2:11 Uhr

  • Warum ist die “Verschwendung” von Speicher ein Problem? Sie sagten, dies sei ein Server, und Server haben heutzutage viel Speicher. Für Servercode ist es in der Regel effizienter, beim Start nur einmal einen Puffer zuzuweisen, der „groß genug“ ist, als ihn für jeden neuen Datenblock neu zuzuweisen.

    – selbe

    27. Dezember 2012 um 10:30 Uhr

Ja:

#include <sys/ioctl.h>

...

int count;
ioctl(fd, FIONREAD, &count);

  • fizzer, ich habe das versucht und ioctl scheint die Anzahl der Bytes auf dem Socket zurückzugeben, aber nicht die Anzahl der Nachrichten. Gibt es eine Möglichkeit, die Anzahl der Nachrichten zu erhalten, oder lese ich diesen Wert falsch? Vielen Dank!

    – Poul

    28. Oktober 2013 um 15:50 Uhr

  • Es gibt nicht wirklich so etwas wie „Nachrichten“ auf der TCP-Schicht, nur einen Strom von Bytes. Ihre Anwendung ist für die Auferlegung zusätzlicher Strukturen verantwortlich.

    – Fizzer

    28. Oktober 2013 um 17:15 Uhr

  • Ist das tragbar? Ist das Teil von BSD Sockets?

    – kyb

    6. Juni 2017 um 13:27 Uhr

Nein, da ist kein. Selbst wenn es eine Möglichkeit gäbe, wäre jede Antwort, die Sie erhalten, sofort veraltet (weil jederzeit neue Daten eintreffen können).

Beachten Sie, dass, wenn Sie einen Puffer an übergeben read()wird die Funktion zurückgegeben, wenn dies der Fall ist irgendein zu lesende Datenmenge (mindestens ein Byte), anstatt darauf zu warten, dass sich der Puffer vollständig füllt.

  • +1. Außerdem möchten Sie dies mit ziemlicher Sicherheit sowieso nicht. Die Verarbeitung von Daten in Cache-freundlichen Blöcken (wenige Kilobyte) ist fast immer schneller als die Verarbeitung großer Blöcke.

    – Nemo

    27. Dezember 2012 um 0:27 Uhr

  • Ich stimme zu. Obwohl ich hinzufügen würde, dass ich denke, dass die Übergabe eines relativ großen Puffers an read() keine schlechte Idee wäre. Sie könnten auch einen größeren Puffer zuweisen, ein bisschen lesen, es in den größeren Puffer speichern und dann ein bisschen mehr lesen. Dies bedeutet jedoch nur, dass die Verarbeitung länger dauert, sobald Sie sie verarbeitet haben.

    – Mats Petersson

    27. Dezember 2012 um 0:29 Uhr

  • @Nemo, wie ist es effizient, mehrere Lesevorgänge in der Reihenfolge von wenigen KB durchzuführen, wenn der Socket 20 MB Daten in die Warteschlange gestellt hat, um gelesen zu werden, im Gegensatz zu einem Systemaufruf, um 20 MB zu entleeren? Und welchen Cache meinst du? L1, L2? ODER Implizieren Sie, dass die Puffergröße ein Vielfaches der Seitengröße sein sollte?

    – Jimm

    27. Dezember 2012 um 0:37 Uhr

  • @Jimm: Ich dachte an L1 (“wenige Kilobyte”), aber das Prinzip gilt im Allgemeinen. Selbst wenn Sie nur einen großen In-Memory-Puffer füllen, wird dies über kleine Lesevorgänge nicht langsamer, da Sie Daten im L1-Cache überschreiben können (in kleinen Puffer lesen, woanders kopieren, erneut in denselben kleinen Puffer lesen). , etc.) Aber Sie werden vermutlich tun etwas mit den Daten, und mein Punkt ist, dass Sie das besser mit kleinen Chunks (frisch im Cache) als mit großen Chunks (veraltet, müssen aus dem RAM abgerufen werden) tun. Die TLB-Verfehlungsstrafe begünstigt auch kleine Puffer.

    – Nemo

    27. Dezember 2012 um 1:11 Uhr


  • @Mats: Es ist schlimmer als du denkst, denke ich. Im Allgemeinen ist das Lesen und Verarbeiten von 1000 8K-Blöcken viel schneller als das Lesen und Verarbeiten eines 8-MB-Blöckes. Dies gilt unabhängig davon, ob Sie eine Berechnung durchführen (z. B. den Durchschnitt nehmen) oder die Daten nur verschieben (z. B. auf die Festplatte oder in einen anderen Socket schreiben). Natürlich gibt es Ausnahmen, aber meiner Erfahrung nach unterschätzen die meisten Programmierer den Leistungseinfluss der Lokalität in modernen Systemen massiv.

    – Nemo

    27. Dezember 2012 um 1:16 Uhr

Du könntest benutzen Nicht blockierende Steckdosen, oder select()/poll() für diese Angelegenheit. Ich bevorzuge nicht blockierende Sockets, weil ich andere Dinge tun kann, während ich auf neue Daten warte.

Dies ist eine “Art von” Antwort: recv(char* buffer, size_t nytes, int flags) wobei flags ODER-verknüpft ist mit:

MSG_PEEK
This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.

So können Sie sehen, ob eine beliebige Anzahl von Bytes im Puffer vorhanden ist, ohne den Puffer irreversibel zu lesen. Dies ist eine halbe Antwort, da dies nicht der effizienteste Weg ist, und MSG_PEEK wird normalerweise verwendet, wenn Nachrichten Header mit bekannter Länge haben, z. B. wie folgt:

000123DT001    

wo 00123 ist die Länge der gesamten Nachricht einschließlich Header, DT ist die Art der Nachricht, und 001 ist die Anzahl der Wiederholungen durch den Absender. Die Idee ist, dass Sie etwas abrufen können, das Ihnen sagt, wie viele Bytes ein vollständiges Lesen einer Nachricht ausmachen. Sie haben kein Interesse an Nachrichten. Aber das ist der Grund dahinter MSG_PEEK

Benutzeravatar von ravi bhuva
Ravi-Bhuva

Sie müssen versuchen, Befehle zu senden und zu empfangen, und Sie können in Sockets Zeichen für Zeichen lesen und schreiben, also keine Speicherverschwendung und noch bessere Kommunikation.

Benutzeravatar von akp
Akp

Ich denke, Sie versuchen, viele Pakete mit einem einzigen Systemaufruf zu erhalten, um den Overhead aufgrund von Systemaufrufen zu reduzieren.

damit du es versuchen kannst PACKET-Sockets-Schnittstellen für Linux 2.4 oder 2.6+ Kernel Versuche dies http://lxr.free-electrons.com/source/Documentation/networking/packet_mmap.txt

1389780cookie-checkSo überprüfen Sie die für einen Socket verfügbare Datenmenge in C und Linux

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

Privacy policy