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

Saj*_*ani 179 c unix sockets posix network-programming

read()和recv()之间的区别,以及socket编程中send()和write()之间的区别是什么?性能和速度等行为.

Gon*_*alo 118

唯一的区别是recv/send允许您为实际操作指定某些选项.读/写是'通用'文件描述符函数,而recv/send稍微更专业(例如,您可以设置一个标志来忽略SIGPIPE,或发送带外消息......).

  • 这是不正确的,对于长度为0的数据报,还有另一个区别-如果长度为零的数据报未决,则flags参数为零的read(2)和recv()提供不同的行为。在这种情况下,read(2)无效(数据报保持未决状态),而recv()使用未决的数据报。 (2认同)
  • @AbhinavGauniyal如何提供_different behavior_?如果有一个0字节的数据报,`recv`和`read`都不会向调用者传递任何数据,但也没有错误.对于调用者,行为是相同的.调用者可能甚至不知道有关数据报的任何信息(它可能不知道这是一个套接字而不是一个文件,它可能不知道这是一个数据报套接字而不是一个流套接字).数据报保持挂起状态是关于IP堆栈如何在内核中工作而对调用者不可见的隐含知识.从来电者的角度来看,他们仍然会提供平等的行为. (2认同)
  • @Mecki这不是每个人的隐含知识,以我为例:) (2认同)
  • @Mecki 非阻塞成功读取 0 字节表示什么?数据报是否仍处于待处理状态?正是这一点,也只有这一点,让我担心:即使成功读取,数据报也可以保持挂起状态。我不确定这种情况是否会出现,因此我想牢记这一点。 (2认同)
  • @sehe如果你担心,为什么不用`recv`?之所以引入`recv`和`send`的原因是并非所有的数据报概念都可以映射到流的世界.`read`和`write`将所有内容视为数据流,无论是管道,文件,设备(例如串行端口)还是套接字.然而,如果套接字使用TCP,则它只是一个真正的流.如果它使用UDP,它更像是一个块设备.但是如果双方都像流一样使用它,它将像流一样工作,你甚至不能使用`write`调用发送一个空的UDP数据包,所以这种情况不会出现. (2认同)

Jon*_*erg 82

按照谷歌的第一次打击

read()等效于带有flags参数0的recv().flange参数的其他值改变了recv()的行为.类似地,write()等同于带有flags == 0的send().

  • 这个帖子现在是谷歌的第一个热门,谷歌喜欢stackoverflow (66认同)
  • 这不是全部.`recv`只能在套接字上使用,如果你试图在`STDIN_FILENO`上使用它,会产生错误. (29认同)
  • @JoeyAdams:在大多数系统上,recv 在非套接字(例如 STDIN_FILENO)上工作得很好。只有少数系统会失败(例如 Windows)。 (2认同)

Bas*_*ard 11

read()并且write()更通用,它们适用于任何文件描述符.但是,它们不适用于Windows.

你可以通过额外的选项来send()recv(),所以您可能需要使用它们在某些情况下.


ajb*_*ajb 7

我刚刚注意到,当我write()在Windows上的套接字上使用它时,它几乎可以工作(传递给的FD与传递给write()它的那个不一样send();我曾经_open_osfhandle()让FD传递给它write()).但是,当我尝试发送包含字符10的二进制数据时,它无效 write().在此之前插入字符13.将其更改为send()使用flags参数0修复了该问题. read()如果13-10在二进制数据中是连续的,则会出现相反的问题,但我还没有测试过.但这似乎是send()和之间的另一个可能的区别write().

  • +1。另见 [winsock 不支持读/写](http://stackoverflow.com/q/4778043) (3认同)

Mer*_*tce 6

Linux上的另一件事是:

send不允许在非插座fd上操作.因此,例如在usb端口上写,write是必要的.


Ric*_*ick 5

在 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);
}
Run Code Online (Sandbox Code Playgroud)