2011년7월22일_Talk Server & Client SIGALRM 시그널 사용시 문제점, 멀티플렉싱과 select( )의 사용법


● Talk Server & Client SIGALRM 시그널 사용시 문제점

ALARM( )에 설정된 시간이 경과 후 발생한 시그널에 의해 서버와 클라이언트의 블로킹함수들,
read( )와 fgets( )가 모두 블로킹이 풀려버려 프로그램이 비정상적인 종료를 한다.

 
하기의 코드는 심각한 오류가 있으니 절대 실행하지 말 것. ㅎㅎ



● talk_server_pipe.c

   1: // talk_server_pipe.c
   2: // TCP 1:1 채텅프로그램
   3: // 프로세스가 올바르게 종료될 수 있도록 부모/자식 프로세스간
   4: // 파이프를 연결하여 종료메시지를 건네준다.
   5: // 특징: 서버와 클라이언트간 메시지가 아닌 동기제어신호를 전송함.
   6: //       read()가 블록이 되지 않으므로 두 가지 장치로 부터 수신가능.
   7: // 
   8: // 작성자: 김수만
   9: // 작성일자: 2011년 7월 21일
  10: // 수정일자: 2011년 7월 22일
  11: #include <stdio.h>
  12: #include <string.h>
  13: #include <stdlib.h>
  14: #include <sys/types.h>
  15: #include <signal.h>
  16: #include <sys/socket.h>
  17: #include <sys/wait.h>
  18: #include <netinet/in.h>
  19: #include <arpa/inet.h>
  20: #include <unistd.h>
  21: #include <signal.h>
  22:  
  23: #define    MAXLINE    512
  24: #define SYNC    1
  25:  
  26: char *escapechar = "exit"; //종료문자열
  27: int server_sock;
  28: int client_sock;
  29: int fd[2];            //파이프
  30:  
  31: void timer(int);        //알람 시그널 처리함수
  32:  
  33: int main(int argc, char *argv[])
  34: {
  35:     int clntlen;
  36:     int num;
  37:     char sendline[MAXLINE];
  38:     char buffer[MAXLINE];
  39:     char recvline[MAXLINE + 1];
  40:     int size;
  41:     pid_t fork_ret;
  42:     struct sockaddr_in client_addr;
  43:     struct sockaddr_in server_addr;
  44:     // signal사용하기 위해 추가된 변수들
  45:     int state;
  46:     struct sigaction act;
  47:  
  48:     if(argc != 2)
  49:     {
  50:         printf("Usage : %s PORT \n", argv[0]);
  51:         exit(0);
  52:     }
  53:  
  54:     // pipe생성
  55:     state = pipe(fd);
  56:     if(-1 == state)
  57:     {
  58:         fprintf(stderr, "pipe() error. \n");
  59:         exit(1);
  60:     }        
  61:     
  62:     // signal설정
  63:     act.sa_handler = timer;
  64:     sigemptyset(&act.sa_mask);
  65:     act.sa_flags = 0;
  66:  
  67:     //타이머 이벤트사용
  68:     if(SIG_ERR == sigaction(SIGALRM, &act, 0))
  69:     {
  70:         fprintf(stderr, "sigaction() error. \n");
  71:         exit(1);
  72:     }
  73:  
  74:     // 소켓 생성
  75:     if((server_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  76:     {
  77:         printf("Server: can't open stream socket. \n");
  78:         exit(0);
  79:     }
  80:  
  81:     // 소켓주소 구조체에 주소 세팅
  82:     bzero((char *)&server_addr, sizeof(server_addr));    //소켓주소 구조체 초기화
  83:     server_addr.sin_family = AF_INET;
  84:     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  85:     server_addr.sin_port = htons(atoi(argv[1]));
  86:  
  87:     // 소켓에 서버 주소 연결
  88:     if(bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
  89:     {
  90:         printf("Server: can't bind local address. \n");
  91:         exit(0);
  92:     }
  93:  
  94:     printf("Server started. \nWaiting for client..\n");
  95:     listen(server_sock, 1);
  96:  
  97:     // 클라이언트의 연결요청 수락
  98:     clntlen = sizeof(client_addr);
  99:     if((client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &clntlen)) < 0)
 100:     {
 101:         printf("Server: failed in acceptiong. \n");
 102:         exit(0);
 103:     }
 104:  
 105:     alarm(1);        //1초 후에 알람시그널 발생
 106:     sleep(1);
 107:     fork_ret = fork();
 108:     if(-1 == fork_ret)
 109:     {
 110:         fprintf(stderr, "fork() error\n");
 111:         exit(1);
 112:     }
 113:     
 114:     if(0 < fork_ret)
 115:     {
 116:         // 부모 프로세스는 키보드 입력을 클라이언트로 송신
 117:         while(1)
 118:         {
 119:             fgets(sendline, MAXLINE, stdin);
 120:             strcpy(buffer, "수만 : ");
 121:             strcat(buffer, sendline);
 122:             size = strlen(buffer);
 123:             if(write(client_sock, buffer, strlen(buffer)) != size)
 124:             {
 125:                 printf("Error in write. \n");
 126:             }
 127:  
 128:             if(strstr(sendline, escapechar) != NULL)    //종료문자열 입력시 처리
 129:             {
 130:                 printf("Good bye. \n");
 131:                 close(client_sock);
 132:                 write(fd[1], escapechar, sizeof(escapechar));
 133:                 sleep(3);        //3초 후 종료
 134:                 waitpid(-1, &state, WNOHANG);    // wait()를 쓰면 더 좋음.
 135:                 printf("부모프로세스 종료!\n");
 136:                 break;                
 137:             }
 138:         }
 139:     }
 140:     else if(0 == fork_ret)
 141:     {
 142:         // 자식 프로세스는 클라이언트로부터 수신된 메시지를 화면에 출력
 143:         while(1)
 144:         {
 145:             // 클라이언트 -> 서버
 146:             if((size = read(client_sock, recvline, MAXLINE)) < 0)
 147:             {
 148:                 printf("Error if read. \n");
 149:                 close(client_sock);
 150:                 printf("자식프로세스 종료! - 1\n");
 151:                 exit(0);
 152:             }
 153:  
 154:             recvline[size] = '\0';
 155:  
 156:             if(strstr(recvline, escapechar) != NULL)    //종료문자열 입력시 처리
 157:             {
 158:                 printf("자식프로세스 종료! - 2\n");
 159:                 break;
 160:             }
 161:             else if(SYNC == recvline[0])    //동기신호
 162:             {
 163:                 // 아무 일도 하지 않음
 164:             }
 165:             else
 166:             {
 167:                 printf("%s", recvline);        //화면 출력
 168:             }
 169:             
 170:             // 자식 <- 부모
 171:             if((size = read(fd[0], recvline, MAXLINE)) < 0)
 172:             {
 173:                 printf("Error if read. \n");
 174:                 close(client_sock);
 175:                 printf("자식프로세스 종료! - 3\n");
 176:                 exit(0);
 177:             }
 178:  
 179:             recvline[size] = '\0';
 180:  
 181:             if(strstr(recvline, escapechar) != NULL)    //종료문자열 입력시 처리
 182:             {
 183:                 //클라이언트에게 이스케이프 문자열 전송
 184:                 write(client_sock, escapechar, sizeof(escapechar));
 185:                 printf("자식프로세스 종료! - 4\n");
 186:                 break;        //자식프로세스 종료
 187:             }
 188:             else if(SYNC == recvline[0])    //동기신호
 189:             {
 190:                 // 아무 일도 하지 않음
 191:             }
 192:             else
 193:             {
 194:                 // 아무 일도 하지 않음.
 195:             }            
 196:         }
 197:     }
 198:     
 199:     close(server_sock);
 200:     close(client_sock);
 201:  
 202:     printf("서버는 정상종료 되었음!\n");
 203:     
 204:     return 0;
 205: }
 206:  
 207: //시그널 처리함수
 208: void timer(int sig)        
 209: {
 210:     int sync = SYNC;
 211:     
 212:     alarm(1);        // 타이머 재시작
 213:     
 214:     write(fd[1], &sync, 1);            // 파이프로 동기신호 발송
 215:     write(client_sock, &sync, 1);    // 클라이언트로 동기신호 발송
 216: }


● talk_client_pipe.c

   1: // talk_client_pipe.c
   2: // TCP 1:1 채텅프로그램
   3: // 프로세스가 올바르게 종료될 수 있도록 부모/자식 프로세스간
   4: // 파이프를 연결하여 종료메시지를 건네준다.
   5: // 특징: 서버와 클라이언트간 메시지가 아닌 동기제어신호를 전송함.
   6: //       read()가 블록이 되지 않으므로 두 가지 장치로 부터 수신가능.
   7: // 
   8: // 작성자: 김수만
   9: // 작성일자: 2011년 7월 21일
  10: // 수정일자: 2011년 7월 22일
  11: #include <stdio.h>
  12: #include <string.h>
  13: #include <stdlib.h>
  14: #include <sys/types.h>
  15: #include <signal.h>
  16: #include <sys/socket.h>
  17: #include <netinet/in.h>
  18: #include <arpa/inet.h>
  19: #include <sys/wait.h>
  20:  
  21: #define    MAXLINE    1024
  22: #define SYNC    1
  23:  
  24: char *escapechar = "exit"; //종료문자열
  25: int s;
  26: int fd[2];                //파이프
  27:  
  28: void timer(int);        //알람 시그널 처리함수
  29:  
  30: int main(int argc, char *argv[])
  31: {
  32:     char line[MAXLINE];
  33:     char sendline[MAXLINE];
  34:     char recvline[MAXLINE + 1];
  35:     char buffer[MAXLINE];
  36:     int n;
  37:     int size;
  38:     int comp;
  39:     int addr_size;
  40:     pid_t fork_ret;
  41:     static struct sockaddr_in server_addr;
  42:     // signal사용하기 위해 추가된 변수들
  43:     int state;
  44:     struct sigaction act;
  45:     
  46:     if(argc != 3)
  47:     {
  48:         printf("Usage : ./%s serverIP serverPORT \n", argv[0]);
  49:         exit(0);
  50:     }
  51:  
  52:     // pipe생성
  53:     state = pipe(fd);
  54:     if(-1 == state)
  55:     {
  56:         fprintf(stderr, "pipe() error. \n");
  57:         exit(1);
  58:     }    
  59:  
  60:     // signal설정
  61:     act.sa_handler = timer;
  62:     sigemptyset(&act.sa_mask);
  63:     act.sa_flags = 0;
  64:  
  65:     //타이머 이벤트사용
  66:     if(SIG_ERR == sigaction(SIGALRM, &act, 0))
  67:     {
  68:         fprintf(stderr, "sigaction() error. \n");
  69:         exit(1);
  70:     }
  71:  
  72:     if(0 != state)
  73:     {
  74:         fprintf(stderr, "sigaction() error. \n");
  75:         exit(1);
  76:     }
  77:     
  78:     // 소켓 생성
  79:     if((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  80:     {
  81:         printf("Client: can't open stream socket. \n");
  82:         exit(0);
  83:     }
  84:  
  85:     // 소켓주소 구조체에 주소 세팅
  86:     bzero((char *)&server_addr, sizeof(server_addr));    //소켓주소 구조체 초기화
  87:     server_addr.sin_family = AF_INET;
  88:     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  89:     server_addr.sin_port = htons(atoi(argv[2]));
  90:  
  91:     // 서버에 연결 요청
  92:     if(connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
  93:     {
  94:         printf("Client: can't connect to server. \n");
  95:         exit(0);
  96:     }
  97:     
  98:     alarm(1);        //1초 후에 알람시그널 발생
  99:     sleep(1);
 100:     fork_ret = fork();
 101:     
 102:     if(0 < fork_ret)
 103:     {
 104:         // 자식 프로세스는 키보드 입력을 클라이언트로 송신
 105:         while(1)
 106:         {            
 107:             fgets(sendline, MAXLINE, stdin);
 108:             strcpy(buffer, "수만: ");
 109:             strcat(buffer, sendline);
 110:             size = strlen(buffer);
 111:             if(write(s, buffer, strlen(buffer)) != size)
 112:             {
 113:                 printf("Error in write. \n");
 114:             }
 115:  
 116:             if(strstr(sendline, escapechar) != NULL)    //종료문자열 입력시 처리
 117:             {
 118:                 printf("Good bye. \n");
 119:                 close(s);
 120:                 sleep(3);        //3초 후 종료
 121:                 waitpid(-1, &state, WNOHANG);    // wait()를 쓰면 더 좋음.
 122:                 exit(0);
 123:             }
 124:         }
 125:     }
 126:     else if(0 == fork_ret)
 127:     {
 128:         // 부모 프로세스는 클라이언트로부터 수신된 메시지를 화면에 출력
 129:         while(1)
 130:         {
 131:             if((size = read(s, recvline, MAXLINE)) < 0)
 132:             {
 133:                 printf("Error if read. \n");
 134:                 close(s);
 135:                 exit(0);
 136:             }
 137:  
 138:             recvline[size] = '\0';
 139:  
 140:             if(strstr(recvline, escapechar) != NULL)    //종료문자열 입력시 처리
 141:             {
 142:                 break;
 143:             }
 144:             else if(SYNC == recvline[0])    //동기신호
 145:             {
 146:                 // 아무 일도 하지 않음
 147:             }
 148:             else
 149:             {
 150:                 printf("%s", recvline);        //화면 출력
 151:             }
 152:             
 153:             // 자식 <- 부모
 154:             if((size = read(fd[0], recvline, MAXLINE)) < 0)
 155:             {
 156:                 printf("Error if read. \n");
 157:                 close(s);
 158:                 exit(0);
 159:             }
 160:  
 161:             recvline[size] = '\0';
 162:  
 163:             if(strstr(recvline, escapechar) != NULL)    //종료문자열 입력시 처리
 164:             {
 165:                 //클라이언트에게 이스케이프 문자열 전송
 166:                 write(s, escapechar, sizeof(escapechar));
 167:                 break;        //자식프로세스 종료
 168:             }
 169:             else if(SYNC == recvline[0])    //동기신호
 170:             {
 171:                 // 아무 일도 하지 않음
 172:             }
 173:             else
 174:             {
 175:                 // 아무 일도 하지 않음
 176:             }
 177:         }
 178:     }
 179:  
 180:     close(s);
 181:  
 182:     return 0;
 183: }
 184:  
 185: //시그널 처리함수
 186: void timer(int sig)        
 187: {
 188:     int sync = SYNC;
 189:     
 190:     alarm(1);        // 타이머 재시작
 191:     
 192:     write(fd[1], &sync, 1);            // 파이프로 동기신호 발송
 193:     write(s, &sync, 1);    // 클라이언트로 동기신호 발송
 194:     
 195:     return ;
 196: }


<실행결과>

image

 

DSCN3730 DSCN3731

image