답변:
차이점은 recv()
/ send()
는 소켓 설명자에서만 작동하며 실제 작업에 대한 특정 옵션을 지정할 수 있다는 것입니다. 이러한 기능은 약간 더 전문화되어 있습니다 (예를 들어, 플래그를 ignore SIGPIPE
또는 대역 외 메시지를 보내도록 설정할 수 있습니다 ).
함수 read()
/ write()
는 모든 디스크립터에서 작동 하는 범용 파일 디스크립터 함수입니다.
recv
와 read
발신자뿐만 아니라 어떤 오류로 데이터를 제공하지 않습니다. 발신자의 경우 동작은 동일합니다. 호출자는 데이터 그램에 대해 전혀 알지 못할 수도 있습니다 (이것은 파일이 아니라 소켓이라는 것을 알 수 없으며 이것이 스트림 소켓이 아닌 데이터 그램 소켓이라는 것을 모를 수도 있습니다). 데이터 그램이 보류 상태를 유지한다는 것은 IP 스택이 커널에서 작동하고 호출자가 볼 수없는 방식에 대한 암묵적인 지식입니다. 발신자의 관점에서, 그들은 여전히 동등한 행동을 제공 할 것입니다.
recv
않습니까? 처음에 왜 recv
그리고 send
어디서 소개 되었는가 는 모든 데이터 그램 개념이 스트림 세계에 매핑 될 수있는 것은 아니기 때문입니다. read
그리고 write
이 파이프, 파일, 디바이스 (예를 들어 직렬 포트) 또는 소켓 여부 데이터의 스트림으로 취급 다. 그러나 소켓은 TCP를 사용하는 경우 실제 스트림 일뿐입니다. UDP를 사용하면 블록 장치와 비슷합니다. 그러나 양쪽 모두 스트림처럼 스트림을 사용하면 스트림처럼 작동하며 write
호출을 사용하여 빈 UDP 패킷을 보낼 수도 없으므로이 상황이 발생하지 않습니다.
read ()는 flags 매개 변수가 0 인 recv ()와 같습니다. flags 매개 변수의 다른 값은 recv ()의 동작을 변경합니다. 마찬가지로 write ()는 플래그 == 0 인 send ()와 같습니다.
recv
는 소켓에서만 사용할 수 있으며, 사용하려고하면 오류가 발생합니다 (예 :) STDIN_FILENO
.
최근 write()
에 Windows의 소켓에서 사용할 때 거의 작동합니다 (전달 된 FD는 전달 write()
된 것과 동일하지 않습니다 .FD를 전달하는 send()
데 사용 _open_osfhandle()
되었습니다 write()
). 그러나 문자 10이 포함 된 이진 데이터를 보내려고 할 때 작동하지 않았습니다. write()
어딘가에 문자 13이 삽입되었습니다. send()
flags 매개 변수를 0으로 변경하면 문제가 해결되었습니다. read()
이진 데이터에서 13-10이 연속적이면 반대의 문제가 발생할 수 있지만 테스트하지는 않았습니다. 하지만 그 사이 또 다른 가능한 차이로 표시 send()
하고 write()
.
리눅스에서 나는 또한 다음을 발견했다.
신호 핸들러에 의한 시스템 호출 및 라이브러리 함수 중단
시스템 호출 또는 라이브러리 함수 호출이 차단되는 동안 신호 핸들러가 호출되면 다음 중 하나를 수행하십시오.
신호 핸들러가 리턴 된 후 호출이 자동으로 다시 시작됩니다. 또는
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).
setockopt (2) : connect (2), send (2), sendto (2) 및 sendmsg (2)를 사용하여 소켓에서 시간 초과 (SO_RCVTIMEO)가 소켓에 설정된 경우 "출력"소켓 인터페이스.
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)
.