Answers:
区别在于recv()
/ send()
仅在套接字描述符上起作用,并且让您为实际操作指定某些选项。这些功能稍微更专业一些(例如,您可以设置一个标志来忽略SIGPIPE
,或发送带外消息...)。
函数read()
/ write()
是适用于所有描述符的通用文件描述符函数。
recv
并且read
不会将任何数据传递给调用方,但是也不会出错。对于呼叫者,行为是相同的。调用者甚至可能不了解数据报(它可能不知道这是套接字而不是文件,它可能不知道这是数据报套接字而不是流套接字)。数据报处于待处理状态是关于IP堆栈如何在内核中工作的隐式知识,并且对于调用者而言是不可见的。从呼叫者的角度来看,它们仍将提供平等的行为。
recv
?之所以recv
并且send
在第一时间推出了地方,并非所有的数据报的概念可以映射到流的世界的事实。read
并将write
所有内容视为数据流,无论是管道,文件,设备(例如串行端口)还是套接字。但是,套接字仅使用TCP时才是实际流。如果使用UDP,则它更像是块设备。但是,如果双方像流一样使用它,它将像流一样工作,并且您甚至不能使用write
调用发送空的UDP数据包,因此不会出现这种情况。
read()
而且write()
更通用,它们可以与任何文件描述符一起使用。但是,它们不适用于Windows。
您可以将其他选项传递给send()
和recv()
,因此在某些情况下可能必须使用它们。
我最近才注意到,当我write()
在Windows中的套接字上使用时,它几乎可以工作(传递给的FD与传递给的FD write()
并不相同send()
;我曾经_open_osfhandle()
让FD传递给write()
)。但是,当我尝试发送包含字符10的二进制数据时,它不起作用 write()
。将其更改send()
为flags参数为0可以解决该问题。 read()
如果13-10在二进制数据中是连续的,则可能会有相反的问题,但我尚未对其进行测试。但这似乎是send()
和之间的另一个可能差异write()
。
在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);
}
#define write(...) send(##__VA_ARGS__, 0)
。