[다중 입출력 함수 select]
I/O 처리하는 경우 non-blocking 으로 여러개의 file descriptor를 체크하면서
읽어들일 데이터가 어떤 상태인지 확인하다가, 어떤 변화가 생기면
그 file descriptor를 리턴하게 된다.
만일 아무런 변화가 없다면 block 상태에 걸려버리는데
이는 timeout을 지정해서 빠져나올 수 있게 한다.
이 기능을 가지고 sleep대신 select를 사용할 수도 있다.
개인적으로 이해가 잘 안되서 이래저래 좀 자세히 테스트 해봤다.
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
내용출처 : http://mintnlatte.tistory.com/313
- nfds : 검사 대상이 되는 fd 범위로 세개의 집합(readfds/writefds/exceptfds)중 가장 큰 파일 디스크립터 값에 1을 더해서 전달하는 매개변수
- readfds : 시스템에 의해 즉시 입력이 가능한지 확인(읽기가 block되지 않았는지 검사하기 위한 리스트/ EOF 발생 검사)
- writefds : 시스템에 의해 즉시 출력이 가능한지 확인(쓰기가 block되지 않았는지 검사하기 위한 리스트)
- exceptfds : 예외가 있는지 검사하기 위한 리스트
- timeout : select가 반환하기 전에 블럭킹될 수 있는 시간의 제한 값 (0은 즉시반환 / NULL은 무한 block)
○ 리턴값
-1 : 오류발생
0 : 타임아웃
양수: 변화 발생 파일 디스크립터 수
: FD_SET 구조체 / 변수 조작하는데 사용되는 함수들
○ FD_SET 구조체
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;
- fd_count : 설정하는 파일 디스크립터의 번호
- fd_array : 설정된 파일 디스크립터의 배열
* fd_set은 0과 1로 저장되는 비트들의 배열이다.
○ FD_SET 변수 조작 함수
- FD_SETFD_ZERO(fd_set *fdset) : fdset 포인터가 가리키는 변수의 모든 비트들을 0으로 초기화
- FD_SET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보 설정(1로 설정)
- FD_CLR(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보를 삭제(0으로 설정)
- FD_ISSET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수가 fd로 전달되는 파일 디스크립터의 정보를 지니는지 확인
○ timeout 구조체
struct timeval {
long tv_sec;
long tv_usec;
}
- tv_sec : 초 단위 설정 변수
- tv_usec : 마이크로 초 단위 설정 변수
동작하는 방식
FD_SET 변수에 변화가 생긴 파일 디스크립터는 1로 세팅, 변화가 없는 파일 디스크립터는 0으로 세팅한다.
- select 함수 호출 전 관찰 대상으로 fd0 과 fd3 이 설정되어 있다.
- select 함수 호출 후 fd3만 1로 설정되어있다.
=> fd3에 변화가 생겼음을 확인 & 해당 파일 디스크립터와 데이터 통신을 진행한다.
다음은 간단한 server/client를 구현했다. 그냥 echo 서버인데 서버쪽에서 여러개의
클라이언트를 그냥 받아들인다.
이걸 기본으로 하면 다른 것도 금방 짜겠다..
소스코드라인도 좀 기네;;;
==================================
서버측 소스코드 #include "sys/socket.h" #include "sys/time.h" #include "sys/types.h" #include "sys/stat.h" #include#include #include #include #include "netinet/in.h" #include "arpa/inet.h" #define FD_MAX_SIZE 1024 int main(int argc, char **argv) { int server_sockfd, client_sockfd, sockfd; int state, client_len; int pid; int i, max_client, maxfd; int client[FD_MAX_SIZE]; FILE *fp; struct sockaddr_in clientaddr, serveraddr; struct timeval tv; fd_set readfds, otherfds, allfds; int current_cli_num; char buf[255]; char line[255]; memset(line, 0x00, 255); state = 0; current_cli_num = 3; if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket error : "); exit(0); } memset(&serveraddr, 0x00, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(9000); state = bind(server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); if (state == -1) { perror("bind error : "); exit(0); } state = listen(server_sockfd, 5); if (state == -1) { perror("listen error : "); exit(0); } client_sockfd = server_sockfd; max_client = -1; maxfd = server_sockfd; for (i = 0; i < FD_MAX_SIZE; i++) { client[i] = -1; } FD_ZERO(&readfds); FD_SET(server_sockfd, &readfds); printf("\nTCP Server Waiting ..."); fflush(stdout); while(1) { allfds = readfds; state = select(maxfd + 1 , &allfds, NULL, NULL, NULL); if (FD_ISSET(server_sockfd, &allfds)) { client_len = sizeof(clientaddr); client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, &client_len); printf("\nconnection from (%s , %d)", inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port)); for (i = 0; i < FD_MAX_SIZE; i++) { if (client[i] < 0) { client[i] = client_sockfd; printf("\nclientNUM=%d\nclientFD=%d\n", i+1, client_sockfd); break; } } printf("accept [%d]\n", client_sockfd); printf("===================================\n"); if (i == FD_MAX_SIZE) { perror("too many clients\n"); } FD_SET(client_sockfd,&readfds); if (client_sockfd > maxfd) maxfd = client_sockfd; if (i > max_client) max_client = i; if (--state <= 0) continue; } for (i = 0; i <= max_client; i++) { if ((sockfd = client[i]) < 0) { continue; } if (FD_ISSET(sockfd, &allfds)) { memset(buf, 0x00, 255); if (read(sockfd, buf, 255) <= 0) { close(sockfd); perror("Close sockfd : "); FD_CLR(sockfd, &readfds); client[i] = -1; } printf("[%d]RECV %s\n", sockfd, buf); write(sockfd, buf, 255); if (--state <= 0) break; } } } }
클라이언트 측 소스코드 #include "sys/socket.h" #include "sys/types.h" #include "netinet/in.h" #include#include #include #include #include #include int main() { int sock, bytes_recieved; char send_data[1024],recv_data[1024]; struct hostent *host; struct sockaddr_in server_addr; host = gethostbyname("127.0.0.1"); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Socket"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(9000); server_addr.sin_addr = *((struct in_addr *)host->h_addr); memset(&(server_addr.sin_zero), 0x00, 8); if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("Connect"); exit(1); } while(1) { printf("\nSEND : "); fgets(send_data, sizeof(send_data), stdin); if (strcmp(send_data , "q") != 0 && strcmp(send_data , "Q") != 0) send(sock,send_data,strlen(send_data), 0); else { send(sock,send_data,strlen(send_data), 0); close(sock); break; } bytes_recieved=recv(sock,recv_data,1024,0); recv_data[bytes_recieved] = '\0'; if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0) { close(sock); break; } else printf("\nRECV = %s\n " , recv_data); } return 0; }
서버측의 결과 화면 TCP Server Waiting ... connection from (127.0.0.1 , 53672) clientNUM=1 clientFD=4 accept [4] =================================== connection from (127.0.0.1 , 53681) clientNUM=2 clientFD=5 accept [5] =================================== [4]RECV nnnnnn [5]RECV aaaaaa
클라이언트 측의 결과 화면 ./aa_client SEND : nnnnnn RECV = nnnnnn
==================================
테스트하고 구현하는 건 자유인데 요러고 나니 아주쬐금 이해가 된다.
'Language > C' 카테고리의 다른 글
poll 함수 예제 (150) | 2012.07.26 |
---|---|
GCC 컴파일러 에러 메세지 리스트(Error Message List) (159) | 2012.07.24 |
Unix Domain Socket 예제 (165) | 2012.07.23 |
semget. semop, semctl 함수 예제 (14) | 2012.07.17 |
mmap / munmap 함수 예제 (12) | 2012.07.17 |