简体   繁体   English

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

[英]What is the Difference Between read() and recv() , and Between send() and write()?

What is the difference between read() and recv() , and between send() and write() in socket programming in terms of performances, speed and other behaviors?就性能、速度和其他行为而言,套接字编程中的read()recv()以及send()write()之间有什么区别?

The difference is that recv() / send() work only on socket descriptors and let you specify certain options for the actual operation.不同之处在于recv() / send()仅适用于套接字描述符,并允许您为实际操作指定某些选项。 Those functions are slightly more specialized (for instance, you can set a flag to ignore SIGPIPE , or to send out-of-band messages...).这些函数稍微更专业(例如,您可以设置一个标志来忽略SIGPIPE ,或者发送带外消息......)。

Functions read() / write() are the universal file descriptor functions working on all descriptors.函数read() / write()适用于所有描述符的通用文件描述符函数。

Per the first hit on Google根据Google 上的第一次点击

read() is equivalent to recv() with a flags parameter of 0. Other values for the flags parameter change the behaviour of recv(). read() 等效于带有 flags 参数为 0 的 recv()。flags 参数的其他值会更改 recv() 的行为。 Similarly, write() is equivalent to send() with flags == 0.类似地,write() 等效于带有标志 == 0 的 send()。

read() and write() are more generic, they work with any file descriptor. read()write()更通用,它们适用于任何文件描述符。 However, they won't work on Windows.但是,它们不适用于 Windows。

You can pass additional options to send() and recv() , so you may have to used them in some cases.您可以将其他选项传递给send()recv() ,因此在某些情况下您可能必须使用它们。

I just noticed recently that when I used write() on a socket in Windows, it almost works (the FD passed to write() isn't the same as the one passed to send() ; I used _open_osfhandle() to get the FD to pass to write() ).我最近才注意到,当我在 Windows 的套接字上使用write()时,它几乎可以工作(传递给write()的 FD 与传递给send()的 FD 不同;我使用_open_osfhandle()来获取FD 传递给write() )。 However, it didn't work when I tried to send binary data that included character 10. write() somewhere inserted character 13 before this.但是,当我尝试发送包含字符 10 的二进制数据时,它不起作用。 write()在此之前插入字符 13 的某处。 Changing it to send() with a flags parameter of 0 fixed that problem.将其更改为带有标志参数 0 的send()解决了该问题。 read() could have the reverse problem if 13-10 are consecutive in the binary data, but I haven't tested it.如果 13-10 在二进制数据中是连续的,则read()可能会出现相反的问题,但我还没有测试过。 But that appears to be another possible difference between send() and write() .但这似乎是send()write()之间另一个可能的区别。

Another thing on linux is: linux上的另一件事是:

send does not allow to operate on non-socket fd. send不允许对非套接字 fd 进行操作。 Thus, for example to write on usb port, write is necessary.因此,例如在 USB 端口上writewrite是必要的。

"Performance and speed"? “性能和速度”? Aren't those kind of ... synonyms, here?这里不是那种……同义词吗?

Anyway, the recv() call takes flags that read() doesn't, which makes it more powerful, or at least more convenient.无论如何, recv()调用采用read()没有的标志,这使它更强大,或者至少更方便。 That is one difference.这是一个区别。 I don't think there is a significant performance difference, but haven't tested for it.我认为没有显着的性能差异,但尚未对其进行测试。

On Linux I also notice that :在 Linux 上,我还注意到:

Interruption of system calls and library functions by signal handlers信号处理程序中断系统调用和库函数
If a signal handler is invoked while a system call or library function call is blocked, then either:如果在系统调用或库函数调用被阻塞时调用信号处理程序,则:

  • the call is automatically restarted after the signal handler returns;信号处理程序返回后自动重新启动调用; or要么

  • the call fails with the error EINTR.调用失败并显示错误 EINTR。

... The details vary across UNIX systems; ... 详细信息因 UNIX 系统而异; below, the details for Linux.下面是 Linux 的详细信息。

If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call is automatically restarted after the signal handler returns if the SA_RESTART flag was used;如果对以下接口之一的阻塞调用被信号处理程序中断,则如果使用了 SA_RESTART 标志,则在信号处理程序返回后调用将自动重新启动; otherwise the call fails with the error EINTR:否则调用失败并显示错误 EINTR:

  • read (2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices. read (2)、readv(2)、write(2)、writev(2) 和 ioctl(2) 在“慢”设备上调用。

..... .....

The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART;以下接口在被信号处理程序中断后永远不会重新启动,无论是否使用 SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler:当被信号处理程序中断时,它们总是以错误 EINTR 失败:

  • "Input" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): accept(2), recv (2), recvfrom (2), recvmmsg(2) (also with a non-NULL timeout argument), and recvmsg(2). “输入”套接字接口,当使用 setockopt(2) 在套接字上设置超时 (SO_RCVTIMEO) 时:accept(2)、 recv (2)、 recvfrom (2)、recvmmsg(2)(也带有非 NULL timeout 参数)和 recvmsg(2)。

  • "Output" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): connect(2), send(2), sendto(2), and sendmsg(2). “输出”套接字接口,当使用 setsockopt(2) 在套接字上设置超时 (SO_RCVTIMEO) 时:connect(2)、send(2)、sendto(2) 和 sendmsg(2)。

Check man 7 signal for more details.检查man 7 signal以获取更多详细信息。


A simple usage would be use signal to avoid recvfrom blocking indefinitely.一个简单的用法是使用信号来避免recvfrom无限期地阻塞。

An example from APUE :来自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);
}

The only difference between recv() and read() is the presence of flags. recv() 和 read() 之间的唯一区别是标志的存在。 With a zero flags argument, recv() is generally equivalent to read()对于零标志参数,recv() 通常等同于 read()

you can use write() and read() instead send() and recv() but send() and recv() offer much greater control over your data transmission您可以使用 write() 和 read() 而不是 send() 和 recv() 但 send() 和 recv() 可以更好地控制您的数据传输

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM