출처 : 열혈 TCP/IP 소켓 프로그래밍 윤성우 저
Linux에서 read/write 함수외에 윈도우에도 존재하는 send와 recv함수를 사용할 수 있다.
- send 함수
ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags);
- recv 함수
ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags);
read/write와의 차이점은 소켓 옵션을 flags로 전달가능한데, 사용가능한 옵션의 예는 다음과 같이 있다.
- MSG_OOB: 긴급 데이터(Out-of-band data)의 전송을 위한 옵션
- MSG_PEEK: 입력 버퍼에 수신된 데이터의 존재 유무를 확인을 위한 옵션
- MSG_DONTROUTE: 데이터 전송과정에서 라우팅 테이블을 참조하지 않음. 로컬 네트워크 상의 목적지를 이용할 때 사용하는 옵션
- MSG_DONTWAIT: 입출력 함수 호출과정에서 블로킹 되지 않을 것을 요구하기 위한 옵션.
- MSG_WAITALL: 요청한 바이트 수에 해당하는 데이터가 전부 수신될 때까지 호출된 함수가 반환되는 것을 막기 위한 옵션.
긴급 데이터 모드
소켓 옵션에 MSG_OOB를 추가하면 긴급 메세지를 전송할 수 있다. 긴급 메세지는 특별한 통신 채널을 쓰는 것이 아니라 수신자 측에서 긴급 메세지 비트 URG를 참조하여 해당 패킷에 대한 데이터를 특별하게 처리해달라는 의미로 사용한다.
- 서버
write(sock, "1234", 4, MSG_OOB);
송신자 코드에는 옵션에 MSG_OOB를 전달하면 된다.
- 클라이언트
긴급 데이터를 수신하는 수신자는 해당 소켓에서 SIGURG 시그널이 발생한다. 따라서 긴급 메세지를 처리하는 시그널 핸들링 함수를 등록해야한다. 게다가 소켓을 여러 프로세스에서 소유할 수 있기 때문에 소켓이 직접 시그널을 전달할 프로세스를 특정해야 올바른 시그널 핸들링이 된다.
fcntl(recv_sock, F_SETOWN, getpid());
위 함수는 recv_sock에서 발생하는 시그널 처리를 getpid(): 현재 실행중인 프로세스가 담당하도록 지정한다.
void urg_handler(int signo);
//...
struct sigaction act;
act.sa_handler = urg_handler;
sigemptyset(&act.sa_mak);
act.sa_flags=0;
//...
// 시그널 등록
fcntl(recv_sock, F_SETOWN, getpid());
int state = sigaction(SIGURG, &act, 0);
- urg_handler
SIGURG를 핸들링하는 함수 내부에서 MSG_OOB를 옵션으로 주어 recv 데이터를 수신하면 긴급 메세지의 마지막 1 바이트만 반환하게 된다.
"1234" 긴급 메세지 -> "4"를 반환
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; // "1234" 중에 "4"만 buf에 쓰여짐
printf("Urgent message : %s \n, buf);
}
URG 패킷은 TCP 헤더에 URG 비트가 1로 설정되어 있고 메세지 길이가 URG_POINTER로 쓰여있다.
입력버퍼 검사
MSG_PEEK: recv 함수 호출시 입력버퍼의 데이터를 읽더라도 기존 버퍼를 지우지 않는 옵션이다.
MSG_DONWAIT: 입력 버퍼 데이터가 없다면 바로 반환하도록하여 블로킹되지 않도록 해준다.
readv와 writev
위 두 함수는 여러 메세지를 한꺼번에 전송하거나 수신하는 함수로 기존 read/write 함수를 여러번 호출하는 것을 절약한다.
ssize_t writev(int filedes, const struct iovec* iov, int iovcnt);
- filedes: 출력 버퍼가 담긴 파일 디스크립터를 전달한다.
- iov: 구조체 iovec의 배열의 주소를 전달한다. iovec에는 전송할 데이터 열과 길이가 담긴다.
- iovcnt: 배열의 길이를 전달한다.
ssize_t readv(int filedes, const struct iovec* iov, int iovcnt);
- filedes: 입력 버퍼가 담긴 파일 디스크립터를 전달한다.
- iov: 구조체 iovec의 배열의 주소를 전달한다. iovec에는 전송할 데이터 열과 길이가 담긴다.
- iovcnt: 배열의 길이를 전달한다.
- iovec 구조체
struct iovec
{
void* iov_base; // 버퍼 주소
size_t iov_len // 버퍼 크기
}
iovec 구조체 하나에 메세지 하나를 담을 수 있으며 내용과 길이를 설정한다.
iovec 구조체 배열을 한꺼번에 전송하기 때문에 writev를 사용하면 여러 메세지를 한꺼번에 일렬로 보낼 수 있다.
'Computer Science 기본 지식 > 소켓 프로그래밍' 카테고리의 다른 글
[TCP/IP 소켓 프로그래밍] (11-1) 다중 접속 서버 - epoll (0) | 2021.04.05 |
---|---|
[TCP/IP 소켓 프로그래밍] (10) 멀티 캐스트와 브로드 캐스트 (0) | 2021.04.01 |
[TCP/IP 소켓 프로그래밍] (8) 다중 접속 서버 - 멀티 플렉싱 기반 select 서버 (0) | 2021.04.01 |
[TCP/IP 소켓 프로그래밍] (7-3) 다중 접속 서버 - 프로세스 간 통신 IPC (0) | 2021.03.31 |
[TCP/IP 소켓 프로그래밍] (7-2) 다중 접속 서버 - 멀티 프로세스 서버/클라이언트 (0) | 2021.03.30 |