在套接字编程中,read()和recv(),以及send()和write()在性能、速度和其他行为方面有什么不同?
read()
recv()
send()
write()
“性能和速度”?那些不是…同义词,在这里吗?
不管怎样,recv()调用采用了read()没有的标记,这使得它更强大,或者至少更方便。这是一个不同之处。我不认为有显著的性能差异,但还没有进行测试。
每谷歌的首支单曲
Read()等效于带有flags参数为0的recv()。flags参数的其他值会改变recv()的行为。类似地,write()等价于带有flags == 0的send()。
区别在于recv()/send()只作用于套接字描述符,并允许你为实际操作指定某些选项。这些函数稍微更专门一些(例如,你可以设置一个标志来忽略SIGPIPE,或者发送带外消息……)。
SIGPIPE
函数read()/write()是工作在所有描述符上的通用文件描述符函数。
你可以将额外的选项传递给send()和recv(),所以在某些情况下你可能不得不使用它们。
我最近才注意到,当我在Windows中的套接字上使用write()时,它几乎可以工作(传递给write()的FD与传递给send()的FD不相同;我使用_open_osfhandle()让FD传递给write())。但是,当我试图发送包含字符10的二进制数据时,它不起作用。write()在此之前插入了字符13。将其更改为带有标志参数为0的send()解决了这个问题。read()可能有相反的问题,如果13-10在二进制数据中是连续的,但我还没有测试它。但这似乎是send()和write()之间的另一个可能的区别。
_open_osfhandle()
linux上的另一件事是:
send不允许操作非套接字fd。因此,例如在usb端口上写入,write是必要的。
send
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).
调用在信号处理程序返回后自动重新启动;或
调用失败,报错EINTR。
... 详细信息因UNIX系统而异;下面是Linux的详细信息。
如果对以下接口之一的阻塞呼叫中断 通过信号处理程序,则调用将在之后自动重新启动 如果使用了SA_RESTART标志,则信号处理程序返回;否则调用失败,错误为EINTR:
.....
以下接口在被信号处理程序中断后永远不会重新启动,不管使用SA_RESTART;他们 当被信号处理程序中断时,总是失败,错误为EINTR:
"输入"套接字接口,当使用setsockopt(2): accept(2), recv(2)在套接字上设置超时(SO_RCVTIMEO)时, recvfrom(2), recvmmsg(2)(也有一个非null的timeout参数),和recvmsg(2).
"Output" socket接口,当timeout (SO_RCVTIMEO)已经在socket上使用setsockopt(2): connect(2), send(2), Sendto (2), sendmsg(2).
查看man 7 signal了解更多细节。
man 7 signal
一个简单的用法是使用信号来避免recvfrom无限期阻塞。
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()对数据传输提供了更大的控制