Unix Network Programming Chapter 14. Advanced I/O Functions 광운대학교컴퓨터과학과 정보통신연구실 석사과정안중현
14.1 Introduction 이장에서소개되고있는내용 I/O operation 에서 timeout 을설정하는세가지방법 세가지 Read/Write 관련함수 recv/send readv/writev recvmsg/sendmsg Ancillary data Socket receive buffer 에서얼마나많은데이터를받을지알아내는방법 소켓에서 C 표준 I/O 라이브러리를사용하는방법 이벤트를기다리기위한좀더진보된방식 2
14.2 Socket Timeouts Socket timeout 을설정하는방법 SIGALRM signal 발생시켜 alarm 호출 정해진시간이끝나면 SIGALRM signal 발생시킴. 다른 signal 과구분하기위한 signal handling 포함 프로세스에서발생하는다른 alarm 에의해영향을받음 select 입출력 Block read write 호출을 blocking 하는대신에시간제한을둔 select 에서입출력을기다리는것을막음 새로운 SO_RCVTIMEO 와 SO_SNDTIMEO 소켓옵션사용 구현시소켓옵션을지원하지않을수있다는문제점 3
Connect with a Timeout Using SIGALRM #include static void "unp.h" connect_alarm(int); int connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec) { Sigfunc *sigfunc; int n; sigfunc = Signal(SIGALRM, connect_alarm); if (alarm(nsec)!= 0) err_msg("connect_timeo: alarm was already set"); 지정한시간이지나면 connect 를호출하는함수 if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0) { close(sockfd); if (errno == EINTR) errno = ETIMEDOUT; alarm(0); /* turn off the alarm */ Signal(SIGALRM, sigfunc); /* restore previous signal handler */ return(n); static void connect_alarm(int signo) { return; /* just interrupt 4 the connect() */
Connect with a Timeout Using SIGALRM // 현재 signal handler 를저장 sigfunc = Signal(SIGALRM, connect_alarm); // 지정한시간만큼알람설정. nsec 는설정시간 if (alarm(nsec)!= 0) err_msg("connect_timeo: alarm was already set"); // connect 실패시 (-1 리턴 ) 소켓을닫고, 인터럽트걸렸을경우 errono 값을 ETIMEDOUT 으로변경 if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0) { close(sockfd); if (errno == EINTR) errno = ETIMEDOUT; // 알람을끄고이전 signal handler로복구 alarm(0); /* turn off the alarm */ Signal(SIGALRM, sigfunc); /* restore previous signal handler */ // signal 처리 connect_alarm(int signo) { return; /* just interrupt the connect() */ 5
recvfrom with a Timeout Using SIGALRM #include static void "unp.h" sig_alrm(int); void dg_cli(file *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; char sendline[maxline], recvline[maxline + 1]; 5 초내에응답이없으면 recvfrom 을일시중지시키는 alarm 함수를호출 Signal(SIGALRM, sig_alrm); while (Fgets(sendline, MAXLINE, fp)!= NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); alarm(5); if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { if (errno == EINTR) fprintf(stderr, "socket timeout n"); else err_sys("recvfrom error"); else { alarm(0); recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); static void sig_alrm(int signo) { return; /* just interrupt the recvfrom() */ 6
recvfrom with a Timeout Using SIGALRM // SIGALRM 을위한 signal handler 를설정 Signal(SIGALRM, sig_alrm); while (Fgets(sendline, MAXLINE, fp)!= NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); // recvfrom 을호출하기전 5 초간기다림 alarm(5); if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { // 만약일시중지된다면에러메시지출력 if (errno == EINTR) fprintf(stderr, "socket timeout n"); else err_sys("recvfrom error"); else { // 서버로부터데이터를받으면 alarm 을해제하고받은데이터출력 alarm(0); recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); // recvform 을일시중지시키기위해사용 static void sig_alrm(int signo) { return; /* just interrupt the recvfrom() */ 7
recvfrom with a Timeout Using select #include "unp.h" int readable_timeo(int fd, int sec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); // 파일디스크립터집합의모든비트를 0으로 FD_SET(fd, &rset); // tv.tv_sec = sec; tv.tv_usec = 0; 읽기가가능해질때까지지정한시간만큼기다림 // 파일디스크립터개수혹은에러리턴 return(select(fd+1, &rset, NULL, NULL, &tv)); /* 4> 0 if descriptor is readable */ 8
recvfrom with a Timeout Using select #include "unp.h 그림 8.8에 Readable_timeo를추가한프로그램 voiddg_cli(file *fp, int sockfd, const SA *pservaddr, socklen_t servlen){ int n; char sendline[maxline], recvline[maxline + 1]; while (Fgets(sendline, MAXLINE, fp)!= NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); if (Readable_timeo(sockfd, 5) == 0) { fprintf(stderr, "socket timeout n"); else { n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); 9
recvfrom with a Timeout Using the SO_RCVTIMEO Socket Optioin void dg_cli(file *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; char sendline[maxline], recvline[maxline + 1]; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (Fgets(sendline, MAXLINE, fp)!= NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); if (n < 0) { if (errno == EWOULDBLOCK) { fprintf(stderr, "socket timeout n"); continue; else err_sys("recvfrom error"); recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); 10
recvfrom with a Timeout Using the SO_RCVTIMEO Socket Optioin // SO_REVTIMEO 는읽기에만해당. 쓰기는 SO_SNDTIMEO. // tv 에지정된시간만큼기다림. Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); // I/O 시간이만료되면, EWOULDBLOCK을 errno로지정하고에러메세지출력 n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); if (n < 0) { if (errno == EWOULDBLOCK) { fprintf(stderr, "socket timeout n"); continue; else err_sys("recvfrom error"); 11
14.3 recv and send Functions 표준함수 read, write 와유사하나인수가하나더필요 #include <sys/socket.h> ssize_t recv (int sockfd, void *buff, size_t nbytes, int flags); ssize_t send (int sockfd, const void *buff, size_t nbytes, int flags); 3 번째인수까지는같음 4 번째인수값 Flags Description recv send MSG_DONTROUTE MSG_DONTWAIT MSG_OOB MSG_PEEK MSG_WAITALL bypass routing table lookup only this operation is nonblocking send or receive out-of-band data peek at incoming message wait for all the data 그림Figure 14.613.6 flags for I/O functions 12
14.3 recv and send Functions MSG_DONTROUTE: kernal 에게 routing table 을살펴볼필요가없음을알림. MSG_DONTWAIT: I/O 에대하여 nonblocking 을설정하지않고, I/O 를수행하며, nonblocking 표시기를해제함을나타낸다. MSG_OOB: out-of-band 에서데이터송신을지정한다. MSG_PEEK: recv 나 recvfrom 에서리턴후데이터를버리지않고살펴볼수있게한다. MSG_WAITALL: kernal 에요구한바이트만큼읽기전에 return 되지말것을지시한다. 13
14.4 readv and writev Functions read, write 와유사하나한번호출로메모리에흩어져있는버퍼로부터읽고쓸수있다. #include <sys/uio.h> ssize_t readv (int filedes, const struct iovec *iov, int iovcnt); ssize_t writev (int filedes, const struct iovec *iov, int iovcnt); Struct iovec { void *iov_base; /* starting address of buffer */ size_t iov_len; /* size of buffer */ ; 14
14.5 recvmsg and sendmsg Functions 입출력함수의가장일반적인형태로 read, readv, recv, recvfrom 을 recvmsg 로대체할수있다. 이함수는대부분의인수를 msghdr 에통합시켰다. #include <sys/socket.h> ssize_t recvmsg (int sockfd, struct msghdr *msg, int flags); ssize_t sendmsg (int sockfd, struct msghdr *msg, int flags); Struct msghdr { void *msg_name; /* protocol address */ ; socklen_t msg_namelen; /* size of protocol address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data; must be aligned for a cmsghdr structure */ socklen_t msg_controllen;/* length of ancillary data */ int msg_flags; /* flags returned by recvmsg() */ 15
14.5 recvmsg and sendmsg Functions msghdr Msg_name 목적지나송신자의프로토콜주소를저장 Recvfrom 과 sendto 의다섯번째와여섯번째인수 주소가지정될필요가없다면 NULL pointer 로 msg_namelen 프로토콜주소의크기 Msg_iov 와 msg_iovlen 입력또는출력버퍼배열을지정. Msg_control 과 msg_controllen 부수적인데이터 (ancillary data) 의위치와크기지정. Msg_flags Msg_flag recvmsg 만사용 Recvmsg 를호출하면 flags 인수는 msg_flags 로복사된다 Msg_flag 는 sendmsg 에서무시된다. 16
14.5 recvmsg and sendmsg Functions Flag MSG_DONTROUTE MSG_DONTWAIT MSG_PEEK MSG_WAITALL MSG_EOR MSG_OOB MSG_BCAST MSG_MCAST MSG_TRUNC MSG_CTRUNC Examined by: Send flags Sendto flags Sendmsg flags Examined by: recv flags recvfrom flags recvmsg flags Returned by: Recvmsg msg_flags 그림 14.7 Figure Summary 13.7 Summary of input of and output flags flags by various by various I/O functions I/O functions 17
14.5 recvmsg and sendmsg Functions 처음네게 flag 는검사될뿐리턴되지않는다. MSG_BCAST: 이 flag 는 BSD/OS 에서새로운것으로링크레이어에서브로드케스트로받다진다면리턴된다. MSG_MCAST: 이 flag 는 BSD/OS 에서새로운것으로데이터그램이링크레이어의멀티케스트로서받아진다면리턴된다. MSG_TRUNC: 이플러그는데이터그램이잘려진다면리턴된다. MSG_CTRUNC: 이플러그는부수적데이터가잘려진다면리턴된다. MSG_EOR: 리턴된데이터가논리적데이터끝이아니면해제된다. MSG_OOB: 이것은 TCP out-of-band 데이터에서절대리턴되지않는다. 18
14.5 recvmsg and sendmsg Functions 프로세스가 UDP 소켓에대해 recvmsg 함수를호출하려는때의데이터구조 msghdr{ msg_name msg_namelen 16 iovec{ msg_iov msg_iovlen 3 iov_base iov_len 100 msg_control msg_controllen msg_flags 20 0 iov_base iov_len iov_base iov_len 60 80 Figure 13.8 Data structures when recvmsg 19 is called for a socket. 14.8 Data structures when recvmsg is called for a UDP
14.5 recvmsg and sendmsg Functions 198.69.10.2에서 206.62.226.35로 170 바이트를보낼때데이터 sockaddr_in{ 16, AF_INET, 2000 198.69.10.2 msghdr{ msg_name msg_namelen msg_iov msg_iovlen msg_control msg_controllen msg_flags 16 3 16 0 iovec{ [] iov_base iov_len iov_base iov_len iov_base iov_len 100 60 80 cmsg_len cmsg_level cmsg_type 16 IPPROTP_IP IP_RECVDSTADDR 206.62.226.35 20 그림 14.9 Update of Figure 14.8 when recvmsg returns
14.6 Ancillary Data Ancillary data 는 sendmsg 와 recvmsg 함수로 msghdr 구조원소를사용하여송수신할수있다 제어정보라고도한다 struct cmsghrd { socklen_t cmsg_len; int cmsg_level; int cmsg_type; // 이구조체의길이 // 전송측프로토콜 // 프로토콜세부타입 Protocol cmsg_level Cmsg_type Description IPv4 IPPROTO_IP IP_RECVDSTADDR receive destination address with UDP datagram IP_RECVIF receive interface index with UDP datagram IPv6 IPPROTO_IPV6 IPV6_DSTOPTS IPV6_HOPLIMIT IPV6_HOPOPTS IPV6_NEXTHOP IPV6_PKTINFO IPV6_RTHDR specify / receive destination options specify / receive hop limit specify / receive hop-by-hop options specify next-hop address specify / receive packet information specify / receive routing header Unix domain SOL_SOCKET SCM_RIGHTS SCM_CREDS send / receive descriptors send / receive user credentials Figure 14.11 summary of uses 21 for ancillary data.
14.6 Ancillary Data msg_control msg_controllen cmsg_len CMSG_LEN() cmsg_len CMSG_LEN() cmsg_len cmsg_level cmsg_type pad data pad cmsg_len cmsg_level cmsg_type pad data cmsghdr{ cmsghdr{ accillary data object CMSG_SPACE() accillary data object CMSG_SPACE() 22 Figure 14.12 Ancillary data containing two ancillary data objects.
14.6 Ancillary Data #include <sys/socket.h> #include <sys/param.h> /* for ALIGN macro on many implementations */ struct cmsghdr *CMSG_FIRSTHDR(struct msghdr * mhdrptr ); struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr ); unsigned char *CMSG_DATA(struct cmsghdr *cmsgptr ); unsigned int CMSG_LEN(unsigned int length); unsigned int CMSG_SPACE(unsigned intlength ); 23
14.7 How much Data Is Queued? 얼마나많은큐가있는지데이터를읽지않고아는세가지방법. 다른일을하려고하기때문에목적이 kernel 에서 block 하는것아니면 15 장에서설명 읽기를원하며프로세스다른쪽에서데이터를읽을수있도록수신대기열에남겨두기원한다면 mgs_peek 를사용한다. 여러 implementation 은 ioctl FIONREAD 의 command 를지원한다. 24
14.8 Sockets and Standard I/O 25