read ()와 recv () 그리고 send ()와 write () 사이의 차이점은 무엇입니까?


198

차이점은 무엇이며 read()그리고 recv(), 사이 send()write()성능, 속도 및 기타 행동의 측면에서 소켓 프로그래밍은?


3
다음과 같이 구현 된 것으로 생각하십시오 #define write(...) send(##__VA_ARGS__, 0).
신중한

답변:


128

차이점은 recv()/ send()는 소켓 설명자에서만 작동하며 실제 작업에 대한 특정 옵션을 지정할 수 있다는 것입니다. 이러한 기능은 약간 더 전문화되어 있습니다 (예를 들어, 플래그를 ignore SIGPIPE또는 대역 외 메시지를 보내도록 설정할 수 있습니다 ).

함수 read()/ write()는 모든 디스크립터에서 작동 하는 범용 파일 디스크립터 함수입니다.


3
길이가 0 인 데이터 그램의 경우 다른 차이점이 있습니다. 길이가 0 인 데이터 그램이 보류중인 경우 0의 플래그 인수를 가진 read (2) 및 recv ()는 다른 동작을 제공합니다. 이 상황에서 read (2)는 영향을 미치지 않으며 (데이터 그램은 보류 상태로 유지됨) recv ()는 보류중인 데이터 그램을 소비합니다.
Abhinav Gauniyal

2
@AbhinavGauniyal 어떻게 다른 행동 을 제공 할까요? 0 바이트 데이터 그램이 있다면, 모두 recvread발신자뿐만 아니라 어떤 오류로 데이터를 제공하지 않습니다. 발신자의 경우 동작은 동일합니다. 호출자는 데이터 그램에 대해 전혀 알지 못할 수도 있습니다 (이것은 파일이 아니라 소켓이라는 것을 알 수 없으며 이것이 스트림 소켓이 아닌 데이터 그램 소켓이라는 것을 모를 수도 있습니다). 데이터 그램이 보류 상태를 유지한다는 것은 IP 스택이 커널에서 작동하고 호출자가 볼 수없는 방식에 대한 암묵적인 지식입니다. 발신자의 관점에서, 그들은 여전히 ​​동등한 행동을 제공 할 것입니다.
Mecki

2
: 예를 들어, 날 데려가, 모두를위한 암묵적 지식이 아니다 @Mecki
Abhinav Gauniyal

1
@Mecki 차단되지 않은 0 바이트 읽기 성공을 나타내는 것은 무엇입니까? 데이터 그램이 여전히 보류 중입니까? 정확하게, 그리고 그것만으로도 걱정입니다 : 데이터 그램이 성공적으로 읽더라도 보류중인 상태입니다. 상황이 발생할 수 있는지 잘 모르겠습니다.이 상황을 명심하고 싶습니다.
sehe

2
@sehe 걱정이 되시면 왜 사용하지 recv않습니까? 처음에 왜 recv그리고 send어디서 소개 되었는가 는 모든 데이터 그램 개념이 스트림 세계에 매핑 될 수있는 것은 아니기 때문입니다. read그리고 write이 파이프, 파일, 디바이스 (예를 들어 직렬 포트) 또는 소켓 여부 데이터의 스트림으로 취급 다. 그러나 소켓은 TCP를 사용하는 경우 실제 스트림 일뿐입니다. UDP를 사용하면 블록 장치와 비슷합니다. 그러나 양쪽 모두 스트림처럼 스트림을 사용하면 스트림처럼 작동하며 write호출을 사용하여 빈 UDP 패킷을 보낼 수도 없으므로이 상황이 발생하지 않습니다.
Mecki

85

Google에서 첫 번째 조회

read ()는 flags 매개 변수가 0 인 recv ()와 같습니다. flags 매개 변수의 다른 값은 recv ()의 동작을 변경합니다. 마찬가지로 write ()는 플래그 == 0 인 send ()와 같습니다.


31
이것은 전체 이야기가 아닙니다. recv는 소켓에서만 사용할 수 있으며, 사용하려고하면 오류가 발생합니다 (예 :) STDIN_FILENO.
Joey Adams

76
이 스레드는 이제 Google에서 첫 번째로 인기를 얻었습니다. Google은 stackoverflow를 좋아합니다
Eloff

12

read()그리고 write()그들은 어떤 파일 기술자와 함께 작동,보다 일반적인입니다. 그러나 Windows에서는 작동하지 않습니다.

send()및에 추가 옵션을 전달할 수 recv()있으므로 경우에 따라 사용해야 할 수도 있습니다.


7

최근 write()에 Windows의 소켓에서 사용할 때 거의 작동합니다 (전달 된 FD는 전달 write()된 것과 동일하지 않습니다 .FD를 전달하는 send()데 사용 _open_osfhandle()되었습니다 write()). 그러나 문자 10이 포함 된 이진 데이터를 보내려고 할 때 작동하지 않았습니다. write()어딘가에 문자 13이 삽입되었습니다. send()flags 매개 변수를 0으로 변경하면 문제가 해결되었습니다. read()이진 데이터에서 13-10이 연속적이면 반대의 문제가 발생할 수 있지만 테스트하지는 않았습니다. 하지만 그 사이 또 다른 가능한 차이로 표시 send()하고 write().



6

리눅스에서 또 다른 것은 :

send소켓이 아닌 fd에서는 작동 할 수 없습니다. 따라서 예를 들어 USB 포트에 쓰려면 write필요합니다.


3

"성능과 속도"? 그런 종류의 동의어가 아닌가?

어쨌든, recv()호출은 read()그렇지 않은 플래그 를 사용하여 더 강력하거나 더 편리합니다. 한 가지 차이점이 있습니다. 성능 차이가 크지 않다고 생각했지만 테스트하지는 않았습니다.


15
아마도 플래그를 다룰 필요가없는 것이 더 편리한 것으로 인식 될 수 있습니다.
semaj

2

리눅스에서 나는 또한 다음을 발견했다.

신호 핸들러에 의한 시스템 호출 및 라이브러리 함수 중단
시스템 호출 또는 라이브러리 함수 호출이 차단되는 동안 신호 핸들러가 호출되면 다음 중 하나를 수행하십시오.

  • 신호 핸들러가 리턴 된 후 호출이 자동으로 다시 시작됩니다. 또는

  • 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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.