socket select/poll
Network/Network 2011. 12. 22. 10:114.2.2 select/poll 의 이용
select/poll 은 입출력 다중화를 위한 목적으로 주로 사용된다. 그러나 이들 함수의 경우 스스로가 Time out 을 결정하기 위한 방법을 제공함으로 비록 입출력다중화의 목적이 아닌 단순한 Time out 결정을 위해서도 유용하게 사용할수 있다.
여기에서는 select 만을 예로 들었는데 poll 로도 비슷하게 구현 가능하다. select 를 이용할경우 alarm() 에 비해서 신뢰성있게 서버를 구성하는게 가능하다. 그러나 입출력다중화 + time out 검사용으로 사용하기에는 적당하지 가 않다. 위에서의 경우에는 단지 하나의 연결에 대해서만 time out 을 검사했는데, 만약 여러개의 연결을 받아들여서 입출력 다중화를 할경우 select 는 모든 입력에 대한 time out 만을 검사함으로, 각각의 개별적인 입력에 대해서는 time out 결과를 알수 없기 때문이다.
#include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <sys/stat.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <sys/time.h> #include <sys/types.h> int main(int argc, char **argv) { int server_sockfd, client_sockfd; int client_len, n; int state; char buf[80]; struct sockaddr_in clientaddr, serveraddr; // select time out 설저을 위한 timeval 구조체 struct timeval tv; fd_set readfds; client_len = sizeof(clientaddr); if ((server_sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket error : "); exit(0); } bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(atoi(argv[1])); bind (server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); listen(server_sockfd, 5); while(1) { memset(buf, 0x00, 80); client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, &client_len); // client_sockfd 의 입력검사를 위해서 // fd_set 에 등록한다. FD_ZERO(&readfds); FD_SET(client_sockfd, &readfds); // 약 5초간 기다린다. tv.tv_sec = 5; tv.tv_usec = 10; // 입력이 있는지 기다린다. state = select(client_sockfd+1, &readfds, (fd_set *)0, (fd_set *)0, &tv); switch(state) { case -1: perror("select error :"); exit(0); // 만약 5초안에 아무런 입력이 없었다면 // Time out 발생상황이다. case 0: printf("Time out error\n"); break; // 5초안에 입력이 들어왔을경우 처리한다. default: if ((n = read(client_sockfd, buf, 80)) <= 0) { perror("read error : "); usleep(10); break; } if (write(client_sockfd, buf, 80) <=0) { perror("write error : "); break; } break; } close(client_sockfd); } }
4.3 연결 소켓 Time out 처리
클라이언트에서 서버로 연결 할 때의 Time out 처리에 대해서 알아보자. select함수만을 이용해서 처리할 수는 없다.connect 함수에서 봉쇄되기 때문이다. 결국 소켓을 비 봉쇄로 만든다음 select함수로 기다려야 한다.
다음과 같은 방식으로 connect 타임아웃을 구현할 것이다. fcntl(2) 함수를 이용해서 듣기소켓을 비 봉쇄 소켓으로 만든다.이제 connect함수를 호출하면 바로 리턴될 것이다. select(2)를 이용해서 timeout을 체크하도록 한다. timeout 체크가 끝난 뒤에는 소켓을 원래의 blocking 상태로 되돌린다.
#include <sys/stat.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> int ConnectWait(int sockfd, struct sockaddr *saddr, int addrsize, int sec) { int newSockStat; int orgSockStat; int res, n; fd_set rset, wset; struct timeval tval; int error = 0; int esize; if ( (newSockStat = fcntl(sockfd, F_GETFL, NULL)) < 0 ) { perror("F_GETFL error"); return -1; } orgSockStat = newSockStat; newSockStat |= O_NONBLOCK; // Non blocking 상태로 만든다. if(fcntl(sockfd, F_SETFL, newSockStat) < 0) { perror("F_SETLF error"); return -1; } // 연결을 기다린다. // Non blocking 상태이므로 바로 리턴한다. if((res = connect(sockfd, saddr, addrsize)) < 0) { if (errno != EINPROGRESS) return -1; } printf("RES : %d\n", res); // 즉시 연결이 성공했을 경우 소켓을 원래 상태로 되돌리고 리턴한다. if (res == 0) { printf("Connect Success\n"); fcntl(sockfd, F_SETFL, orgSockStat); return 1; } FD_ZERO(&rset); FD_SET(sockfd, &rset); wset = rset; tval.tv_sec = sec; tval.tv_usec = 0; if ( (n = select(sockfd+1, &rset, &wset, NULL, NULL)) == 0) { // timeout errno = ETIMEDOUT; return -1; } // 읽거나 쓴 데이터가 있는지 검사한다. if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset) ) { printf("Read data\n"); esize = sizeof(int); if ((n = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&esize)) < 0) return -1; } else { perror("Socket Not Set"); return -1; } fcntl(sockfd, F_SETFL, orgSockStat); if(error) { errno = error; perror("Socket"); return -1; } return 1; } int main(int argc, char **argv) { struct sockaddr_in serveraddr; int sockfd; int len; if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { perror("error"); return 1; } serveraddr.sin_family = AF_INET; serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1"); serveraddr.sin_port = htons(atoi(argv[1])); len = sizeof(serveraddr); if (ConnectWait(sockfd, (struct sockaddr *)&serveraddr, len, 5) < 0) { perror("error"); } else { printf("Connect Success\n"); } while(1) { sleep(1); } close(sockfd); }
'Network > Network' 카테고리의 다른 글
Socket 함수 정리 (0) | 2011.12.22 |
---|---|
block & non-block (0) | 2011.12.22 |
IP Subnet (0) | 2011.12.02 |
멀티캐스트 원리 (0) | 2011.12.02 |
gethostbyname() 도메인 이름으로 hostent 정보를 구함 (0) | 2011.12.01 |