Qual è la differenza tra read () e recv () e tra send () e write ()?


198

Qual è la differenza tra read()e recv(), e tra send()e write()nella programmazione socket in termini di prestazioni, velocità e altri comportamenti?


3
Pensate di scrittura come implementata in questo modo: #define write(...) send(##__VA_ARGS__, 0).
carefulnow1

Risposte:


128

La differenza è che recv()/ send()funziona solo con i descrittori di socket e consente di specificare determinate opzioni per l'operazione effettiva. Tali funzioni sono leggermente più specializzate (ad esempio, è possibile impostare un flag da ignorare SIGPIPEo per inviare messaggi fuori banda ...).

Le funzioni read()/ write()sono le funzioni del descrittore di file universale che funzionano su tutti i descrittori.


3
Questo non è corretto, c'è un'altra differenza nel caso di datagrammi di lunghezza 0 - Se è in sospeso un datagramma di lunghezza zero, leggi (2) e recv () con un argomento flag di zero forniscono un comportamento diverso. In questa circostanza, read (2) non ha alcun effetto (il datagramma rimane in sospeso), mentre recv () consuma il datagramma in sospeso.
Abhinav Gauniyal,

2
@AbhinavGauniyal In che modo fornirebbe comportamenti diversi ? Se c'è un datagramma da 0 byte, entrambi, recve readnon fornirà dati al chiamante ma anche nessun errore. Per il chiamante, il comportamento è lo stesso. Il chiamante potrebbe anche non sapere nulla dei datagrammi (potrebbe non sapere che si tratta di un socket e non di un file, potrebbe non sapere che si tratta di un socket di datagramma e non di un socket di flusso). Il fatto che il datagramma rimanga in sospeso è una conoscenza implicita del funzionamento degli stack IP nei kernel e non visibile al chiamante. Dal punto di vista del chiamante, forniranno comunque un comportamento uguale.
Mecki,

2
@Mecki che non è una conoscenza implicita per tutti,
prendimi

1
@Mecki cosa indica una lettura riuscita non bloccante di 0 byte? Il datagramma rimane ancora in sospeso? Esattamente quello, e solo quello, mi preoccupa: il comportamento che un datagramma può rimanere in sospeso anche se letto con successo. Non sono sicuro che la situazione possa insorgere, per cui mi piacerebbe tenerlo a mente.
visto il

2
@sehe Se sei preoccupato, perché non usi recv? Il motivo per cui recve senddove è stato introdotto in primo luogo è stato il fatto che non tutti i concetti di datagramma potevano essere mappati sul mondo dei flussi. reade writeconsidera tutto come un flusso di dati, sia esso una pipe, un file, un dispositivo (ad esempio una porta seriale) o un socket. Tuttavia, un socket è un flusso reale solo se utilizza TCP. Se utilizza UDP è più simile a un dispositivo a blocchi. Ma se entrambe le parti lo usano come un flusso, funzionerà come un flusso e non è nemmeno possibile inviare un pacchetto UDP vuoto usando le writechiamate, quindi questa situazione non si presenterà.
Mecki,

85

Per il primo successo su Google

read () equivale a recv () con un parametro flags di 0. Altri valori per il parametro flags cambiano il comportamento di recv (). Allo stesso modo, write () equivale a send () con flags == 0.


31
Questa non è l'intera storia. recvpuò essere utilizzato solo su un socket, e produrrà un errore se si tenta di utilizzarlo su, diciamo, STDIN_FILENO.
Joey Adams,

77
Questo thread è ora il primo successo su Google, Google ama StackOverflow
Eloff

12

read()e write()sono più generici, funzionano con qualsiasi descrittore di file. Tuttavia, non funzioneranno su Windows.

Puoi passare opzioni aggiuntive a send()e recv(), quindi potresti doverle utilizzare in alcuni casi.


7

Ho notato di recente che quando ho usato write()su un socket in Windows, funziona quasi (l'FD passato write()non è lo stesso di quello passato send(); ero solito _open_osfhandle()far passare l'FD write()). Tuttavia, non ha funzionato quando ho provato a inviare dati binari che includevano il carattere 10. write()da qualche parte ha inserito il carattere 13 prima di questo. Modificandolo in send()con un parametro flags di 0 risolto quel problema. read()potrebbe avere il problema inverso se 13-10 sono consecutivi nei dati binari, ma non l'ho provato. Ma questa sembra essere un'altra possibile differenza tra send()e write().



6

Un'altra cosa su Linux è:

sendnon consente di operare su fd senza socket. Pertanto, ad esempio per scrivere sulla porta USB, writeè necessario.


2

"Prestazioni e velocità"? Quel tipo di ... sinonimi non sono qui?

Comunque, la recv()chiamata prende flag che read()non lo fanno, il che la rende più potente, o almeno più conveniente. Questa è una differenza. Non penso che ci sia una differenza significativa nelle prestazioni, ma non l'ho testato.


15
Forse non dover trattare con le bandiere può essere percepito come più conveniente.
semaj

2

Su Linux noto anche che:

Interruzione delle chiamate di sistema e delle funzioni di libreria da parte dei gestori di segnale
Se un gestore di segnale viene richiamato mentre una chiamata di sistema o una chiamata di funzione di libreria viene bloccata, allora:

  • la chiamata viene riavviata automaticamente dopo il ritorno del gestore del segnale; o

  • la chiamata fallisce con l'errore EINTR.

... I dettagli variano tra i sistemi UNIX; sotto, i dettagli per Linux.

Se una chiamata bloccata a una delle seguenti interfacce viene interrotta da un gestore di segnale, la chiamata viene riavviata automaticamente dopo che il gestore di segnale ritorna se è stato utilizzato il flag SA_RESTART; altrimenti la chiamata fallisce con l'errore EINTR:

  • leggi (2), readv (2), write (2), writev (2) e ioctl (2) su dispositivi "lenti".

.....

Le seguenti interfacce non vengono mai riavviate dopo essere state interrotte da un gestore di segnale, indipendentemente dall'uso di SA_RESTART; falliscono sempre con l'errore EINTR quando vengono interrotti da un gestore di segnale:

  • Interfacce socket "Input", quando un timeout (SO_RCVTIMEO) è stato impostato sul socket utilizzando setsockopt (2): accept (2), recv (2), recvfrom (2), recvmmsg (2) (anche con un non-NULL argomento timeout) e recvmsg (2).

  • Interfacce socket "Output", quando un timeout (SO_RCVTIMEO) è stato impostato sul socket utilizzando setsockopt (2): connect (2), send (2), sendto (2) e sendmsg (2).

Controlla man 7 signalper maggiori dettagli.


Un semplice utilizzo sarebbe usare il segnale per evitare il recvfromblocco indefinito.

Un esempio da 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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.