在套接字编程中,read()和recv(),以及send()和write()在性能、速度和其他行为方面有什么不同?


“性能和速度”?那些不是…同义词,在这里吗?

无论如何,recv()调用接受read()不接受的标志,这使得它更强大,或者至少更方便。这是一个不同之处。我不认为有显著的性能差异,但还没有进行测试。


根据谷歌的第一次点击

Read()等效于带有flags参数为0的recv()。flags参数的其他值会改变recv()的行为。类似地,write()等价于带有flags == 0的send()。


区别在于recv()/send()只作用于套接字描述符,并允许您为实际操作指定某些选项。这些函数稍微专业化一些(例如,您可以设置一个标志来忽略SIGPIPE,或者发送带外消息……)。

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


Read()和write()更通用,它们适用于任何文件描述符。 但是,它们不能在Windows上运行。

您可以将其他选项传递给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()). However, it didn't work when I tried to send binary data that included character 10. write() somewhere inserted character 13 before this. Changing it to send() with a flags parameter of 0 fixed that problem. read() could have the reverse problem if 13-10 are consecutive in the binary data, but I haven't tested it. But that appears to be another possible difference between send() and write().


linux上的另一件事是:

Send不允许对非套接字fd进行操作。因此,例如在usb端口上写入,写入是必要的。


在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. ... The details vary across UNIX systems; below, the details for 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; otherwise the call fails with the error EINTR: read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices. ..... The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler: "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). "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).

查看人员7号信号了解更多细节。


一个简单的用法是使用信号来避免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);
}

recv()和read()之间的唯一区别是是否存在标志。使用零标志参数,recv()通常等效于read()


您可以使用write()和read()来代替send()和recv(),但是send()和recv()对数据传输提供了更大的控制