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

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

128135 次浏览

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

不管怎样,recv()调用采用了read()没有的标记,这使得它更强大,或者至少更方便。这是一个不同之处。我不认为有显著的性能差异,但还没有进行测试。

谷歌的首支单曲

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

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

函数read()/write()是工作在所有描述符上的通用文件描述符函数。

read()write()更通用,它们适用于任何文件描述符。 但是,它们不能在Windows上工作

你可以将额外的选项传递给send()recv(),所以在某些情况下你可能不得不使用它们。

我最近才注意到,当我在Windows中的套接字上使用write()时,它几乎可以工作(传递给write()的FD与传递给send()的FD不相同;我使用_open_osfhandle()让FD传递给write())。但是,当我试图发送包含字符10的二进制数据时,它不起作用。write()在此之前插入了字符13。将其更改为带有标志参数为0的send()解决了这个问题。read()可能有相反的问题,如果13-10在二进制数据中是连续的,但我还没有测试它。但这似乎是send()write()之间的另一个可能的区别。

linux上的另一件事是:

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

在Linux上,我还注意到:

< p > 信号处理程序中断系统调用和库函数 < br > 如果在系统调用或库函数调用被阻塞时调用了信号处理程序,则:

  • 调用在信号处理程序返回后自动重新启动;或

  • 调用失败,报错EINTR。

... 详细信息因UNIX系统而异;下面是Linux的详细信息。

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

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

.....

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

  • "输入"套接字接口,当使用setsockopt(2): accept(2), recv(2)在套接字上设置超时(SO_RCVTIMEO)时, recvfrom(2), recvmmsg(2)(也有一个非null的timeout参数),和recvmsg(2).

    . >(2)
  • "Output" socket接口,当timeout (SO_RCVTIMEO)已经在socket上使用setsockopt(2): 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);
}

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

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