Qual é a diferença entre read () e recv () e Between send () e write ()?

198

Qual é a diferença entre read()e recv(), e entre send()e write()na programação do soquete em termos de performances, velocidade e outros comportamentos?

Sajad Bahmani
fonte
3
Pense em gravação como implementado assim: #define write(...) send(##__VA_ARGS__, 0).
precisa

Respostas:

128

A diferença é que recv()/ send()funciona apenas em descritores de soquete e permite especificar determinadas opções para a operação real. Essas funções são um pouco mais especializadas (por exemplo, você pode definir um sinalizador para ignorar SIGPIPEou enviar mensagens fora da banda ...).

Funções read()/ write()são as funções universais de descrição de arquivos que funcionam em todos os descritores.

Gonzalo
fonte
3
Isso está incorreto, há outra diferença no caso de datagramas com tamanho 0 - se um datagrama com comprimento zero estiver pendente, read (2) e recv () com um argumento flags de zero fornecem um comportamento diferente. Nessa circunstância, a leitura (2) não tem efeito (o datagrama permanece pendente), enquanto recv () consome o datagrama pendente.
Abhinav Gauniyal
2
@AbhinavGauniyal Como isso proporcionaria um comportamento diferente ? Se houver um datagrama de 0 byte, ambos, recve readnão fornecerá dados ao chamador, mas também nenhum erro. Para o chamador, o comportamento é o mesmo. O chamador pode nem saber nada sobre datagramas (pode não saber que esse é um soquete e não um arquivo, pode não saber que esse é um soquete de datagrama e não um soquete de fluxo). O fato de o datagrama permanecer pendente é um conhecimento implícito sobre como as pilhas IP funcionam nos kernels e não são visíveis para o chamador. Da perspectiva do chamador, eles ainda fornecerão um comportamento igual.
Mecki
2
@Mecki isso não é conhecimento implícito para todos, me leve por exemplo :)
Abhinav Gauniyal
1
@Mecki, o que indica uma leitura bem-sucedida sem bloqueio de 0 bytes? O datagrama ainda permanece pendente? Exatamente isso, e apenas isso, está me preocupando: o comportamento que um datagrama pode ficar pendente, mesmo que seja lido com êxito. Não tenho certeza se a situação pode surgir, e é por isso que eu gostaria de ter isso em mente.
sehe
2
@sehe Se você está preocupado, por que não usa recv? A razão pela qual recve sendonde foi introduzida em primeiro lugar foi o fato de que nem todos os conceitos de datagrama puderam ser mapeados para o mundo dos fluxos. reade writetrate tudo como um fluxo de dados, seja um canal, um arquivo, um dispositivo (por exemplo, uma porta serial) ou um soquete. No entanto, um soquete é apenas um fluxo real se ele usa TCP. Se ele usa UDP, é mais como um dispositivo de bloco. Mas se os dois lados o usarem como um fluxo, ele funcionará como um fluxo e você não poderá nem enviar um pacote UDP vazio usando writechamadas, para que essa situação não ocorra.
Mecki
85

Pela primeira vez no Google

read () é equivalente a recv () com um parâmetro flags de 0. Outros valores para o parâmetro flags alteram o comportamento de recv (). Da mesma forma, write () é equivalente a send () com sinalizadores == 0.

Jonathan Feinberg
fonte
31
Esta não é a história toda. recvsó pode ser usado em um soquete e produzirá um erro se você tentar usá-lo, digamos STDIN_FILENO,.
Joey Adams
76
Esta discussão é agora o primeiro hit no Google, o Google adora stackoverflow
Eloff
12

read()e write()são mais genéricos, eles funcionam com qualquer descritor de arquivo. No entanto, eles não funcionarão no Windows.

Você pode passar opções adicionais para send()e recv(), portanto, pode ser necessário usá-las em alguns casos.

Bastien Léonard
fonte
7

Acabei de notar recentemente que, quando usei write()um soquete no Windows, quase funcionou (o FD passou para write()não é o mesmo que o passou para send(); eu costumava _open_osfhandle()fazer o FD para passar write()). No entanto, não funcionou quando tentei enviar dados binários que incluíam o caractere 10. Em write()algum lugar, o caractere 13 foi inserido antes disso. Mudá-lo para send()com um parâmetro flags de 0 corrigiu esse problema. read()poderia ter o problema inverso se 13-10 forem consecutivos nos dados binários, mas eu não testei. Mas isso parece ser outra diferença possível entre send()e write().

ajb
fonte
2
+1. Veja também winsock não apoiar leitura / gravação
Joseph Quinsey
6

Outra coisa no linux é:

sendnão permite operar em fd sem soquete. Assim, por exemplo, para escrever na porta USB, writeé necessário.

Mert Mertce
fonte
3

"Desempenho e velocidade"? Esse tipo de ... não é sinônimo, aqui?

De qualquer forma, a recv()chamada recebe sinalizadores que read()não o fazem, o que a torna mais poderosa ou pelo menos mais conveniente. Essa é uma diferença. Não acho que exista uma diferença significativa de desempenho, mas ainda não testei.

descontrair
fonte
15
Talvez não ter que lidar com bandeiras possa ser percebido como mais conveniente.
semaj 24/11/2009
2

No Linux, também noto que:

Interrupção de chamadas do sistema e funções de biblioteca por manipuladores de sinal
Se um manipulador de sinal for chamado enquanto uma chamada de sistema ou chamada de função de biblioteca estiver bloqueada, então:

  • a chamada é reiniciada automaticamente após o retorno do manipulador de sinal; ou

  • a chamada falha com o erro EINTR.

... Os detalhes variam entre os sistemas UNIX; abaixo, os detalhes para Linux.

Se uma chamada bloqueada para uma das seguintes interfaces for interrompida por um manipulador de sinal, a chamada será reiniciada automaticamente depois que o manipulador de sinal retornar se o sinalizador SA_RESTART foi usado; caso contrário, a chamada falhará com o erro EINTR:

  • read (2), readv (2), write (2), writev (2) e ioctl (2) chama dispositivos "lentos".

.....

As seguintes interfaces nunca são reiniciadas após serem interrompidas por um manipulador de sinal, independentemente do uso de SA_RESTART; eles sempre falham com o erro EINTR quando interrompidos por um manipulador de sinal:

  • Interfaces de soquete "Input", quando um tempo limite (SO_RCVTIMEO) foi definido no soquete usando setsockopt (2): accept (2), recv (2), recvfrom (2), recvmmsg (2) (também com um valor não NULL argumento de tempo limite) e recvmsg (2).

  • Interfaces de soquete "Output", quando um tempo limite (SO_RCVTIMEO) foi definido no soquete usando setsockopt (2): connect (2), send (2), sendto (2), sendto (2) e sendmsg (2).

Verifique man 7 signalpara mais detalhes.


Um uso simples seria usar o sinal para evitar o recvfrombloqueio indefinidamente.

Um exemplo de 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);
}
Rick
fonte