[RTMP] 다양한 입출력 함수들
다양한 입출력 함수
리눅스는 소켓도 파일로 간주해서 파일 입출력 함수인 read()와 write()를 이용해서 데이터를 송수신할 수 있습니다. 하지만 윈도우는 파일 입출력 팜수와 소켓 입출력 함수가 구분되어 있습니다. 그래서 윈도우에서는 read()/write()이 아닌 send()/recv()를 사용해서 소켓을 송수신합니다.
본 포스팅에서는 리눅스에서 send()/recv()를 사용하는 경우와 추가적인 입출력 함수들에 대해서 알아보겠습니다.
리눅스에서의 send & recv
윈도우에서의 send/recv와 동일하고 선언된 자료형의 이름에서만 차이가 납니다.
#include <sys/socket.h>
/**
* @param socketfd 데이터 전송 대상과의 연결을 의미하는 소켓의 fd전달
* @param buf 전송할 데이터를 저장하고 있는 버퍼의 주소 값 전달
* @param nbytes 전송할 바이트 수 전달
* @param flags 데이터 전송시 적용할 다앙한 옵션 정보 전달
*/
ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags);
/**
* @param buf 수신된 데이터를 저장할 버퍼의 주소 값 전달
* @param nbytes 수신할 수 있는 최대 바이트 수 전달
* @param flags 데이터 수신 시 적용할 다양한 옵션 정보 전달.
*
*/
ssize_t recv(int sockfd, void * buf, size_t nbytes, int flags);
마지막 매개변수 옵션은 비트 연산자 OR( | ) 을 이용해서 둘 이상의 내용을 함께 전달할 수 있습니다. |
옵션 | 의미 | send | recv |
---|---|---|---|
MSG_OOB | 긴급 데이터Out Of Band data의 전송을 위한 옵션 | 가능 | 가능 |
MSG_PEEK | 입력버퍼에 수신된 데이터의 존재유무 확인을 위한 옵션 | 가능 | |
MSG_DONTROUTE | 데이터 전송과정에서 라우팅 테이블을 참조하지 않을 것을 요구하는 옵션, 따라서 로컷 네트워크 상에서 목적지를 찾을 때 사용되는 옵션 | 가능 | |
MSG_DONTWAIT | 입출력함수 호출과정에서 블로킹되지 않을 것을 요구하기 위한 옵션. 즉, 넌블로킹 IO의 요구에 사용되는 옵션 | 가능 | 가능 |
MSG_WAITALL | 요청한 바이트 수에 해당하는 데이터가 전부 수실될 때까지 호출된 함수가 반환되는 것을 막기 위한옵션 | 가능 |
MSG_OOB
MSG_OOB를 통해서 send/recv의 flag를 어떤 식으로 적용하기 위해서 알아보겠습니다. Out Of Band 데이터는 긴급한 메시지를 보내고 주고 받는 옵션입니다. 먼저 보내는 입장에서는 이렇게 합니다.
send(sock, "4", strlen("4"), MSG_OOB);
옵션이 read/write가 아닌 send/recv에 있는 옵션이기 때문에 send를 사용했고, 마지막 인자로 MSG_OOB를 전달하면 됩니다.
MSG_OOB를 받는 입장에서는 다음과 같이 진행합니다.
int main ( - ) {
struct sigaction act;
int state;
//액션 핸들러 등록
act.sa_handler=urg_handler;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
(일반적인 소켓 생성 로직)
recv_sock=accept(acpt_sock, (struct sockaddr*)&serv_adr, &serv_adr_sz);
//recv_sock이 가리키는 소켓의 소유자(F_SETOWN)을 getidf()함수가 반환하는 ID의 프로세스로 변경
fcntl(recv_sock, F_SETOWN, getpid());
state=sigaction(SIGURG, &act, 0);
(일반적인 recv 로직)
}
void urg_handler(int signo){
int str_len;
char buf[BUF_SIZE];
str_len=recv(recv_sock, buf, sizeof(buf)-1, MSG_OOB);
buf[str_len]=0;
printf("Urgent message: %s \n", buf);
}
소켓은 운영체제가 생성 및 관리하기 때문에 엄밀히 따지면 소켓의 소유자는 운영체제입니다. 하지만 여기서의 소유자는 이 소켓에서 발생하는 모든 일의 책임 주체를 의미합니다. 위 상황에서는 recv_sock 소켓에 의해 발생하는 SIGURG 시그널을 처리하는 프로세스는 getpid 함수가 반환하는 ID의 프로세스로 변경하겠다는 말입니다. 멀티프로세스가 실행중인 경우 fork된 여러 개의 프로세스가 하나의 소켓의 소유자가 되면 안되기 때문에 이렇게 오너 프로세스를 지정해줍니다.
MSG_OOB 모드로 데이터를 전달하는 경우 딱 1바이트만 반환됩니다. 그리고 모든 MSG_OOB가 아닌 데이터모다 먼저 도착하는 것이 보장되지도 않습니다. 긴급 메시지라는 의미 보다는 Out-of-band 형태로 전송되려면 별도의 통신 경로가 확보되어야 하는데 TCP는 별도의 통신 경로를 제공하지 않습니다. 따라서 TCP의 urgent mode라는 것을 이용해서 데이터를 전송할 뿐입니다.
readv / writev
두 함수는 데이터를 모아서 전송하고 모아서 수신하는 기능을 가지고 있습니다. 읽을 때는 여러 개의 버퍼배열에 나누어서 읽고, 보낼 때는 여러 개의 배열에 데이터가 있을 때 각각에 대해서 write을 수행하지 않고 한 번에 보냅니다.
예를 들어 3개의 버퍼에 출력할 데이터가 들어있고, Nagle알고리즘을 OFF한 상황을 생각해보겠습니다. 아마 총 세개의 패킷이 생성되어서 전송될 것입니다. 반면에 writev를 사용하면 한 번에 모든 데이터를 출력버퍼로 밀어넣기 때문에 하나의 패킷만 생성되어서 전송될 확률이 높습니다.