read()和recv()之间以及send()和write()之间有什么区别?


Answers:


128

区别在于recv()/ send()仅在套接字描述符上起作用,并且让您为实际操作指定某些选项。这些功能稍微更专业一些(例如,您可以设置一个标志来忽略SIGPIPE,或发送带外消息...)。

函数read()/ write()适用于所有描述符的通用文件描述符函数。


3
这是不正确的,对于长度为0的数据报,还有另一个区别-如果零长度的数据报未决,则flags参数为零的read(2)和recv()提供不同的行为。在这种情况下,read(2)无效(数据报保持未决状态),而recv()使用未决的数据报。
Abhinav Gauniyal '02

2
@AbhinavGauniyal将如何提供不同的行为?如果同时存在一个0字节的数据报,recv并且read不会将任何数据传递给调用方,但是也不会出错。对于呼叫者,行为是相同的。调用者甚至可能不了解数据报(它可能不知道这是套接字而不是文件,它可能不知道这是数据报套接字而不是流套接字)。数据报处于待处理状态是关于IP堆栈如何在内核中工作的隐式知识,并且对于调用者而言是不可见的。从呼叫者的角度来看,它们仍将提供平等的行为。
梅基'17

2
@Mecki并不是每个人的隐性知识,以我为例:)
Abhinav Gauniyal

1
@Mecki 0字节的非阻塞成功读取表示什么?数据报是否仍保持待处理状态?恰恰是,这让我担心:数据报即使成功读取也可能保持挂起的行为。我不确定这种情况是否会发生,我想牢记这一点。
sehe

2
@sehe如果您担心,为什么不使用recv?之所以recv并且send在第一时间推出了地方,并非所有的数据报的概念可以映射到流的世界的事实。read并将write所有内容视为数据流,无论是管道,文件,设备(例如串行端口)还是套接字。但是,套接字仅使用TCP时才是实际流。如果使用UDP,则它更像是块设备。但是,如果双方像流一样使用它,它将像流一样工作,并且您甚至不能使用write调用发送空的UDP数据包,因此不会出现这种情况。
Mecki

85

先打上谷歌

read()等效于flags参数为0的recv()。flags参数的其他值会更改recv()的行为。同样,write()等同于带有标志== 0的send()。


31
这还不是全部。 recv只能在套接字上使用,并且如果尝试在套接字上使用则会产生错误STDIN_FILENO
乔伊·亚当斯

76
此线程现在是Google上的第一条
命题

12

read()而且write()更通用,它们可以与任何文件描述符一起使用。但是,它们不适用于Windows。

您可以将其他选项传递给send()recv(),因此在某些情况下可能必须使用它们。


7

我最近才注意到,当我write()在Windows中的套接字上使用时,它几乎可以工作(传递给的FD与传递给的FD write()并不相同send();我曾经_open_osfhandle()让FD传递给write())。但是,当我尝试发送包含字符10的二进制数据时,它不起作用 write()。将其更改send()为flags参数为0可以解决该问题。 read()如果13-10在二进制数据中是连续的,则可能会有相反的问题,但我尚未对其进行测试。但这似乎是send()和之间的另一个可能差异write()



6

Linux上的另一件事是:

send不允许在非套接字fd上运行。因此,例如必须在USB端口上进行写入write


3

“性能和速度”?这些不是...同义词吗?

无论如何,该recv()调用带有read()不带有的标志,这使其功能更强大,或者至少更加方便。那是一个区别。我认为没有明显的性能差异,但尚未对此进行测试。


15
也许不必处理标志可能会被认为更方便。
semaj

2

在Linux上,我还注意到:

信号处理程序中断系统调用和库函数
如果在系统调用或库函数调用被阻止时调用信号处理程序,则:

  • 信号处理程序返回后,呼叫将自动重新启动;要么

  • 呼叫失败,并显示错误EINTR。

...详细信息在UNIX系统上有所不同;下面是Linux的详细信息。

如果信号处理程序中断了对以下接口之一的阻塞调用,则如果使用了SA_RESTART标志,则在信号处理程序返回后,该调用将自动重新启动;否则,将重新启动该调用。否则,调用将失败,并显示错误EINTR:

  • 在“慢速”设备上的read(2),readv(2),write(2),writev(2)和ioctl(2)调用。

.....

不管使用SA_RESTART,以下接口在被信号处理程序中断后都不会重新启动;当被信号处理程序中断时,它们总是失败并显示错误EINTR:

  • 当使用setsockopt(2)在套接字上设置了超时(SO_RCVTIMEO)时,将使用“输入”套接字接口:accept(2), recv(2), recvfrom(2),recvmmsg(2)(也具有非NULL)超时参数)和recvmsg(2)。

  • 使用setsockopt(2)在套接字上设置超时(SO_RCVTIMEO)时,将使用“输出”套接字接口:connect(2),send(2),sendto(2)和sendmsg(2)。

检查man 7 signal更多细节。


一种简单的用法是使用信号以避免recvfrom无限期地阻塞。

来自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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.