Was ist der Unterschied zwischen read() und recv() und zwischen send() und write()?

Lesezeit: 6 Minuten

Was ist der Unterschied zwischen read() und recv()und dazwischen send() und write() in der Socket-Programmierung in Bezug auf Leistung, Geschwindigkeit und andere Verhaltensweisen?

  • Stellen Sie sich Write wie folgt vor: #define write(...) send(##__VA_ARGS__, 0).

    – Vorsicht jetzt1

    7. Januar 2017 um 9:39 Uhr


Gonzalos Benutzeravatar
Gonzalo

Der Unterschied ist das recv()/send() arbeiten nur mit Socket-Deskriptoren und lassen Sie bestimmte Optionen für die eigentliche Operation angeben. Diese Funktionen sind etwas spezialisierter (z. B. können Sie ein Flag auf Ignorieren setzen SIGPIPEoder um Out-of-Band-Nachrichten zu senden…).

Funktionen read()/write() sind die Universal- Dateideskriptorfunktionen, die auf alle Deskriptoren wirken.

  • Dies ist falsch, es gibt einen weiteren Unterschied im Fall von Datagrammen der Länge 0 – Wenn ein Datagramm der Länge null ansteht, liefern read(2) und recv() mit einem flags-Argument von null ein anderes Verhalten. In diesem Fall hat read(2) keine Wirkung (das Datagramm bleibt anhängig), während recv() das anstehende Datagramm verbraucht.

    – Abhinav Gauniyal

    11. Februar 2017 um 8:44 Uhr

  • @AbhinavGauniyal Wie würde das aussehen unterschiedliches Verhalten? Wenn es ein 0-Byte-Datagramm gibt, beide, recv und read liefert dem Anrufer keine Daten aber auch keinen Fehler. Für den Anrufer ist das Verhalten dasselbe. Der Aufrufer weiß möglicherweise nicht einmal etwas über Datagramme (er weiß möglicherweise nicht, dass dies ein Socket und keine Datei ist, er weiß möglicherweise nicht, dass dies ein Datagramm-Socket und kein Stream-Socket ist). Dass das Datagramm ausstehend bleibt, ist implizites Wissen darüber, wie IP-Stacks in Kerneln funktionieren, und für den Aufrufer nicht sichtbar. Aus der Sicht des Anrufers bieten sie immer noch gleiches Verhalten.

    – Mecki

    19. Juli 2017 um 16:51 Uhr

  • @Mecki das ist nicht für jeden implizites Wissen, nimm mich zum Beispiel 🙂

    – Abhinav Gauniyal

    20. Juli 2017 um 12:12 Uhr

  • @Mecki was zeigt ein nicht blockierendes erfolgreiches Lesen von 0 Bytes an? Bleibt das Datagramm noch ausstehend? Genau das, und nur das, macht mir Sorgen: das Verhalten, dass ein Datagramm ausstehend bleiben kann, selbst wenn es erfolgreich gelesen wurde. Ich bin mir nicht sicher, ob die Situation eintreten kann, weshalb ich sie gerne im Hinterkopf behalten möchte.

    – sehen

    20. September 2018 um 13:28 Uhr


  • @sehe Wenn Sie sich Sorgen machen, warum verwenden Sie nicht recv? Der Grund warum recv und send An erster Stelle wurde die Tatsache eingeführt, dass nicht alle Datagramm-Konzepte auf die Welt der Streams abgebildet werden konnten. read und write Behandeln Sie alles als Datenstrom, egal ob es sich um eine Pipe, eine Datei, ein Gerät (z. B. eine serielle Schnittstelle) oder einen Socket handelt. Ein Socket ist jedoch nur dann ein echter Stream, wenn er TCP verwendet. Wenn es UDP verwendet, ist es eher ein Blockgerät. Aber wenn beide Seiten es wie einen Stream verwenden, funktioniert es wie ein Stream und Sie können nicht einmal ein leeres UDP-Paket mit senden write Anrufe, damit diese Situation nicht eintritt.

    – Mecki

    20. September 2018 um 15:31 Uhr


Pro der erste Treffer bei Google

read() entspricht recv() mit einem flags-Parameter von 0. Andere Werte für den flags-Parameter ändern das Verhalten von recv(). In ähnlicher Weise entspricht write() send() mit Flags == 0.

  • Das ist nicht die ganze Geschichte. recv kann nur auf einem Socket verwendet werden und erzeugt einen Fehler, wenn Sie versuchen, es beispielsweise auf STDIN_FILENO.

    – Joey Adams

    31. Juli 2011 um 5:29 Uhr

  • Dieser Thread ist jetzt der erste Treffer bei Google, Google liebt Stackoverflow

    – Elof

    8. Januar 2013 um 21:14 Uhr

read() und write() sind allgemeiner, sie funktionieren mit jedem Dateideskriptor. Sie funktionieren jedoch nicht unter Windows.

Sie können zusätzliche Optionen an übergeben send() und recv()daher müssen Sie sie möglicherweise in einigen Fällen verwenden.

Das ist mir erst kürzlich aufgefallen, als ich es benutzt habe write() an einem Socket in Windows funktioniert es fast (die FD übergeben an write() ist nicht dasselbe wie das, an das weitergegeben wurde send(); ich benutzte _open_osfhandle() um die FD zu übergeben write()). Es funktionierte jedoch nicht, als ich versuchte, Binärdaten zu senden, die das Zeichen 10 enthielten. write() irgendwo eingefügtes Zeichen 13 davor. Ändern Sie es zu send() mit einem flags-Parameter von 0 wurde dieses Problem behoben. read() könnte das umgekehrte Problem haben, wenn 13-10 in den Binärdaten aufeinanderfolgend sind, aber ich habe es nicht getestet. Aber das scheint ein weiterer möglicher Unterschied zwischen zu sein send() und write().

Eine andere Sache unter Linux ist:

send erlaubt keinen Betrieb auf Nicht-Socket-fd. So zum Beispiel auf USB-Port schreiben, write ist notwendig.

Unter Linux fällt mir auch auf, dass:

Unterbrechung von Systemaufrufen und Bibliotheksfunktionen durch Signalhandler

Wenn ein Signalhandler aufgerufen wird, während ein Systemaufruf oder ein Bibliotheksfunktionsaufruf blockiert ist, dann entweder:

  • der Anruf wird automatisch neu gestartet, nachdem der Signalhandler zurückkehrt; oder

  • der Aufruf schlägt mit dem Fehler EINTR fehl.

… Die Details variieren je nach UNIX-System; unten die Details für Linux.

Wenn ein blockierter Aufruf an eine der folgenden Schnittstellen von einem Signalhandler unterbrochen wird, wird der Aufruf automatisch neu gestartet, nachdem der Signalhandler zurückkehrt, wenn das Flag SA_RESTART verwendet wurde; ansonsten schlägt der Aufruf mit dem Fehler EINTR fehl:

  • lesen(2), readv(2), write(2), writev(2) und ioctl(2) rufen “langsame” Geräte auf.

…..

Die folgenden Schnittstellen werden niemals neu gestartet, nachdem sie von einem Signalhandler unterbrochen wurden, unabhängig von der Verwendung von SA_RESTART; Sie schlagen immer mit dem Fehler EINTR fehl, wenn sie von einem Signalhandler unterbrochen werden:

  • “Eingabe”-Socket-Schnittstellen, wenn ein Timeout (SO_RCVTIMEO) auf dem Socket mit setsockopt(2) gesetzt wurde: accept(2), Empf(2),
    recvfrom(2), recvmmsg(2) (ebenfalls mit einem Timeout-Argument ungleich NULL) und recvmsg(2).

  • “Output”-Socket-Schnittstellen, wenn ein Timeout (SO_RCVTIMEO) auf dem Socket mit setsockopt(2) gesetzt wurde: connect(2), send(2), sendto(2) und sendmsg(2).

Prüfen man 7 signal für mehr Details.


Eine einfache Verwendung wäre, Signal zu verwenden, um es zu vermeiden recvfrom unbefristet sperren.

Ein Beispiel aus APUE:

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>

#define BUFLEN      128
#define TIMEOUT     20

void
sigalrm(int signo)
{
}

void
print_uptime(int sockfd, struct addrinfo *aip)
{
    int     n;
    char    buf[BUFLEN];

    buf[0] = 0;
    if (sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)
        err_sys("sendto error");
    alarm(TIMEOUT);
    //here
    if ((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0) {
        if (errno != EINTR)
            alarm(0);
        err_sys("recv error");
    }
    alarm(0);
    write(STDOUT_FILENO, buf, n);
}

int
main(int argc, char *argv[])
{
    struct addrinfo     *ailist, *aip;
    struct addrinfo     hint;
    int                 sockfd, err;
    struct sigaction    sa;

    if (argc != 2)
        err_quit("usage: ruptime hostname");
    sa.sa_handler = sigalrm;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGALRM, &sa, NULL) < 0)
        err_sys("sigaction error");
    memset(&hint, 0, sizeof(hint));
    hint.ai_socktype = SOCK_DGRAM;
    hint.ai_canonname = NULL;
    hint.ai_addr = NULL;
    hint.ai_next = NULL;
    if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
        err_quit("getaddrinfo error: %s", gai_strerror(err));

    for (aip = ailist; aip != NULL; aip = aip->ai_next) {
        if ((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0)) < 0) {
            err = errno;
        } else {
            print_uptime(sockfd, aip);
            exit(0);
        }
    }

    fprintf(stderr, "can't contact %s: %s\n", argv[1], strerror(err));
    exit(1);
}

Benutzeravatar von unwind
entspannen

“Leistung und Geschwindigkeit”? Sind das nicht so … Synonyme hier?

Jedenfalls die recv() call nimmt das flaggen read() nicht, was es leistungsfähiger oder zumindest bequemer macht. Das ist ein Unterschied. Ich glaube nicht, dass es einen signifikanten Leistungsunterschied gibt, habe es aber nicht getestet.

  • Vielleicht wird es als bequemer empfunden, sich nicht mit Flaggen befassen zu müssen.

    – semaj

    24. November 2009 um 16:16 Uhr

1426260cookie-checkWas ist der Unterschied zwischen read() und recv() und zwischen send() und write()?

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

Privacy policy