ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TCP/IP 소켓 프로그래밍 - 13장 : 다양한 입출력 함수들
    네트워크 2024. 6. 24. 23:58

    13장

    13-1 send & recv 입출력 함수

    리눅스에서는 read, write 함수 말고 recv , send 함수를 이용해서 데이터를 송수신할 수 있다.

    send함수는 아래와 같이 생겼다.


    #include <sys/socket.h>
    
    ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags);
    //성공 시 전송된 바이트 수, 실패 시 -1 리턴
    //flags : 데이터 전송 시 적용할 옵션 정보 전달
    //nbytes : 전송할 바이트 수 전달

     

     

    recv함수는 아래와 같이 생겼다.

    #include <sys/socket.h>
    
    ssize_t recv(int sockfd, void * buf, size_t nbytes, int flags);
    //성공 시 수신한 바이트 수( EOF 수신 시 0 ), 실패 시 -1 리턴
    //nbytes : 수신할 수 있는 최대 바이트 수 전달

     

    마지막 매개 변수에는 비트 연산자를 이용해 여러 개를 함께 전달할 수 있다.

     

    그 중 MSG_OOB는 긴급 메시지 전송을 위해 사용된다.

    MSG_OOB는 긴급 메시지를 처리하기 위해 메시지의 전송방법, 경로를 바꾸고자 할 때 사용된다.

     

    예제( oob_recv.c )를 작성하다보면, 아래와 같은 코드를 만나게 된다.

    fcntl(recv_sock, F_SETOWN, getpid());

     

    위 코드가 의미하는 바는 ,

    파일 디스크립터 recv_sock과 대응되는

    소켓에서 발생한 시그널(여기서는 SIGURG)을 처리하는 프로세스

    getpid()함수가 반환하는 프로세스로 변경시키겠다는 의미이다.

     

    위 코드가 의미하는 바를 다시 정리하면,

    현재 실행중인 프로세스를 SIGURG 시그널의 처리 주체로 지정하는 것이다.

     

    out-of-band data (oob data)는 전혀 다른 통신 경로로 전송되는 데이터를 의미한다.

     

    MSG_OOB 옵션을 쓴다고 해서 더 빨리 데이터가 전송되지도 않고,

    urg_handler 함수에서도 1바이트밖에 읽히지 않는다. ( 나머지는 일반 입력 함수를 통해 읽힌다. )

     

    TCP에서는 별도의 통신 경로를 제공하지 않고,

    다만 Urgent mode를 이용해 데이터를 전송해준다.


    Urgent mode의 동작원리

    MSG_OOB는 데이터를 수신하는 대상에게 데이터의 빠른 처리를 재촉한다.

    위 그림은 예제 oob_send.c 32행 실행 후 출력 버퍼 상황이다. ( 그림에서는 값을 살짝 바꿨다. )

     

    send( sock, “123”, strlen (”123”), MSG_OOB );

     

    버퍼의 가장 오른쪽 문자, 바로 오른편 위치Urgent Pointer로 지정한다.

     

    Urgent Pointer는 자신이 가리키는 오프셋 바로 앞의 메시지가 긴급 메시지임을 알려준다.

    긴급 메시지 정보는 실제로 하나의 바이트에만 표시가 된다.

    TCP header에는

    1. URG = 1 긴급 메시지가 존재하는 패킷임을 알림.
    2. URG Pointer Urgent Pointer의 위치가 오프셋 3에 위치해 있다.

    라는 정보가 담겨 있다.

    긴급 메시지가 “123”인지, “23”인지는 중요하지 않다.

    긴급 메시지는 메시지 처리를 재촉하는 데 의미가 있을 뿐이다.


    입력버퍼 검사

    입력버퍼에 수신된 데이터가 존재하는지 확인하는 옵션은 MSG_PEEK , MSG_DONTWAIT이 쓰인다.

     

    MSG_PEEK 옵션을 주고 recv함수를 호출할 경우,

    데이터를 읽더라도 입력버퍼에서 데이터가 지워지지 않는다.

     

    이를 이용하면, MSG_DONTWAIT 옵션과 함께

    블로킹되지 않으면서 데이터의 존재유무를 확인하는 함수 구성에 사용될 수 있다.

     

    아래 사진은 예제 peek_send.c 와 peek_recv.c를 실행한 결과이다.

     

    첫번째 recv 함수호출 시 MSG_PEEK 옵션을 지정했기 때문에 입력버퍼가 지워지지 않아,

    두번째 recv 함수 호출 시 입력버퍼의 값을 읽으며 한 번 더 출력이 가능했다.

     


     

    13-2 readv & writev 입출력 함수

     

    readv, writev함수는 데이터 송수신 효율성을 향상시킬 수 있다.

    writev 함수를 사용하면 여러 버퍼에 나뉘어 저장되어있는 데이터를 한번에 전송가능하고,

    readv 함수를 사용하면 데이터를 여러 버퍼에 나눠서 수신할 수 있다.

     

    #include <sys/uio.h>
    
    ssize_t writev(int filedes, const struct iovec *iov ,int iovcnt);
    //성공 시 전송된 바이트 수, 실패 시 -1 반환
    
    //filedes : 데이터 전송의 목적지를 나타내는 파일 디스크립터 전달. 소켓에만 제한된 함수가 아님.
    //iov : 구조체 iovec 배열의 주소 값 전달
    //iovcnt : 두번째 인자로 전달된 주소 값이 가리키는 배열의 길이정보

     

     

    구조체 iovec ( writev의 2번째 인자관련 ) 은 다음과 같이 생겼다.

    struct iovec
    {
    	void * iov_base; // 버퍼 주소 정보
    	size_t iov_len;  // 버퍼 크기 정보
    }

    writev 함수를 그림으로 표현하면 아래와 같다.

    writev ( 1, ptr, 2 ); 에서

    첫번째 인자 1은 “표준 출력”에 대한 파일 디스크립터를 나타내므로, 콘솔에 출력이 될 것이다.

    ptr은 전송할 데이터를 모아둔 iovec 배열을 가리키는 포인터이다.

    세번째 인자 2는 총 2개의 iovec 배열을 가리키게 된다는 뜻이다.

     

     

    ptr[0]의 iov_base는 a로 시작하는 문자열을 가리키며, iov_len = 3이므로 “abc”가 전송된다.

    ptr[1]의 iov_base는 1로 시작하는 문자열을 가리키며, iov_len = 2이므로 “12”가 뒤따라 전송된다.

    writev.c 예제 코드 일부를 적으면 다음과 같다.

    ...
    struct iovec vec[2];
    char buf1[] = "ABCDEFG";
    char buf2[] = "1234567";
    int str_len;
    
    vec[0].iov_base = buf1;
    vec[0].iov_len = 3;
    vec[1].iov_base = buf2;
    vec[1].iov_base = 4;
    
    str_len = writev(1, vec, 2);
    ...

     

     

    writev.c 예제 코드 실행 결과는 아래와 같다.

     

    readv함수는 아래와 같이 생겼다.

    #include <sys/uio.h>
    
    ssize_t readv(int filedes, const struct iovec * iov, int iovcnt);
    //성공 시 수신된 바이트 수, 실패시 -1 반환
    //filedes : 수신받을 파일디스크립터

     

     

    아래는 readv.c 예제 코드 실행 결과이다.

     

     

    전송해야 할 데이터가 여러 개의 버퍼에 나뉘어 있는 경우,

    여러 번의 write 함수 호출 보다 한번의 writev 함수 호출이 효율적이다.

     

    입력버퍼에 수신된 데이터를 여러 저장소에 나눠서 읽어 들이고 싶은 경우,

    여러 번 read 함수를 호출하는 것보다 한 번 readv 함수를 호출하는 것이 효율적이다.

     

    Writev 함수는 nagle 알고리즘이 중지된 상황에서 더 가치가 크다.

    전송되는 패킷의 수를 줄일 수 있기 때문이다.

Designed by Tistory.