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?
Was ist der Unterschied zwischen read() und recv() und zwischen send() und write()?
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 SIGPIPE
oder 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
undread
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 warumrecv
undsend
An erster Stelle wurde die Tatsache eingeführt, dass nicht alle Datagramm-Konzepte auf die Welt der Streams abgebildet werden konnten.read
undwrite
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 sendenwrite
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 aufSTDIN_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);
}
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
Stellen Sie sich Write wie folgt vor:
#define write(...) send(##__VA_ARGS__, 0)
.– Vorsicht jetzt1
7. Januar 2017 um 9:39 Uhr