(윤성우 저, 'TCP/IP 소켓 프로그래밍' - 10장 관련 내용입니다)
Select 함수를 이용하여 주어진 서버와 통신하는 I/O 멀티플렉싱 클라이언트를 작성하는 문제입니다.
- 일정 시간 내에 데이터를 입력하지 않을 경우 Time Out 메시지 출력
- read_routine 함수와 write_routine함수 적절히 호출
아래는 주어진 서버(multi_serv.c) 코드입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/select.h> #define BUF_SIZE 100 void error_handling(char *buf); int main(int argc, char *argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_adr, clnt_adr; struct timeval timeout; fd_set reads, cpy_reads; socklen_t adr_sz; int fd_max, str_len, fd_num, i; char buf[BUF_SIZE]; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } serv_sock=socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1) error_handling("bind() error"); if(listen(serv_sock, 5)==-1) error_handling("listen() error"); FD_ZERO(&reads); FD_SET(serv_sock, &reads); fd_max=serv_sock; while(1) { cpy_reads=reads; timeout.tv_sec=5; timeout.tv_usec=5000; if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1) break; if(fd_num==0) continue; for(i=0; i<fd_max+1; i++) { if(FD_ISSET(i, &cpy_reads)) { if(i==serv_sock) // connection request! { adr_sz=sizeof(clnt_adr); clnt_sock= accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz); FD_SET(clnt_sock, &reads); if(fd_max<clnt_sock) fd_max=clnt_sock; printf("connected client: %d \n", clnt_sock); } else // read message! { str_len=read(i, buf, BUF_SIZE); if(str_len==0) // close request! { FD_CLR(i, &reads); close(i); printf("closed client: %d \n", i); } else { write(i, buf, str_len); // echo! } } } } } close(serv_sock); return 0; } void error_handling(char *buf) { fputs(buf, stderr); fputc('\n', stderr); exit(1); } |
주어진 multi_server 코드에 맞는 echo_client를 구현하기 위해 아래와 같은 코드를 작성했습니다.
코드 설명입니다.
1. 먼저 서버와 통신하기 위한 기본적인 과정을 거칩니다. (socket 만들고 connect)
2. select함수를 사용하기 위해 초기화와 타임아웃 값 설정 과정을 거칩니다.
3. select함수를 이용하여 입력 디스크립터(0번)과 소켓 디스크립터를 감시하여 수신된 데이터의 존재 여부를 검사합니다.
4. 수신된 데이터의 없다면 "Time-out!"을 출력하고 수신된 데이터가 있다면 입력, 출력 여부에 맞게 처리해줍니다.
아래는 구현한 echo_client 코드입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/select.h> #define BUF_SIZE 30 void error_handling(char *message); void read_routine(int sock, char *buf); void write_routine(int sock, char *buf); int main(int argc, char *argv[]) { int sock; char buf[BUF_SIZE]; struct sockaddr_in serv_adr; //타이머를 계산하기 위해 timeval형 변수 timeout 설정 struct timeval timeout; //fd_set형 변수 선언하여 파일 디스크립터 정보 등록 fd_set reads, cpy_reads; //검사할 파일 디스크립터의 수 int fd_max, fd_num; if(argc!=3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } sock=socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2])); if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("connect() error!"); //fd_set형 변수 reads의 모든 비트를 0으로 초기화 FD_ZERO(&reads); //입력 및 소켓 디스크립터 정보를 등록 FD_SET(0, &reads); FD_SET(sock, &reads); //소켓의 번호를 저장 fd_max = sock; while(1) { cpy_reads = reads; //타임아웃 시간 설정 timeout.tv_sec=5; timeout.tv_usec=0; //소켓 포함 모든 파일디스크립터를 대상으로 '수신된 데이터의 존재여부' 검사 fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout); //오류가 발생했다면 if(fd_num==-1) { error_handling("select() error\n"); } //Timeout이 발생했다면 else if (fd_num == 0) { puts("Time-out!"); continue; } //변화가 발생했다면 else { //만약 입력 디스크립터에 변화가 발생했다면 (사용자로부터 데이터를 입력받았다면) if(FD_ISSET(0, &cpy_reads)) { //입력된 내용을 서버로 전송하는 함수 write_routine(sock, buf); //파일 디스크립터 정보 삭제 FD_CLR(0, &cpy_reads); } //만약 포트에 변화가 발생했다면(서버로부터 데이터를 받았다면) if(FD_ISSET(sock, &cpy_reads)) { //받은 데이터를 출력하는 함수 read_routine(sock, buf); //파일 디스크립터 정보 삭제 FD_CLR(sock, &cpy_reads); } } } close(sock); return 0; } void read_routine(int sock, char *buf) { int str_len=read(sock, buf, BUF_SIZE); if(str_len==0) return; buf[str_len]=0; printf("Message from server: %s", buf); } void write_routine(int sock, char *buf) { fgets(buf, BUF_SIZE, stdin); if(!strcmp(buf,"q\n") || !strcmp(buf,"Q\n")) { shutdown(sock, SHUT_WR); exit(0); } write(sock, buf, strlen(buf)); } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } |
'학교 수업 및 과제 > 컴퓨터 네트워크' 카테고리의 다른 글
3. I/O 멀티플렉싱 서버와 멀티프로세스 클라이언트를 이용한 채팅 프로그램 (0) | 2018.05.28 |
---|---|
1. Iterative Echo Server 만들기 (0) | 2018.05.19 |