9 장. 파이프 Unix 프로그래밍및실습 1
강의내용 1 절개요 2 절이름없는파이프 3 절이름있는파이프 http://lily.mmu.ac.kr/lecture/13u2/ch09.pdf 책에나온내용반드시 man 으로확인할것! UNIX, LINUX 등시스템마다차이가있을수있음을반드시인식 2
기본실습 #1 [ 예제 9-1] ~ [ 예제 9-7] ( 각 10점 ) 과제개요 (2줄이상 ) 프로그램 실행화면캡처 보고서첫장에다음표작성 과제완성여부 (O, X) 비고 예제 9-1 (10) 예제 9-2 (10) 예제 9-7 (10) O O O X 3
다중입출력 키보드와파이프, 소켓을동시에처리해야하는경우가 대부분 순차적으로각 I/O 를확인하는방식의경우해당장치에서 읽을내용이없는경우입력이올때까지대기 (Blocking) 다중입출력처리시나리오 하나이상의파일기술자하나가준비될때까지잠든다 깨어나서순차적으로준비된파일기술자확인후처리 다시반복 4
select() 동기식다중입출력메커니즘제공 #include <sys/time.h> #include <sys/types.h> #include <unistd.h> 읽기가준비되었는지확인할 fd 집합 쓰기가준비되었는지확인할 fd 집합 최대대기시간 int select(int n, fd_set *readfds, fd_set, *writefds, fd_set *exceptfds, struct timeval *timeout); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set); 예외또는대역외자료 ( 소켓만 ) 가있는지확인할 fd 집합 세집합에속한 fd 들중에입출력중하나이상의 fd 가 가능하면반환 / timeout 까지없는경우역시반환 하나이상의 fd 에서입출력이가능할수있으므로각 fd_set 의 내용을순차적으로확인해야함 5
파이프채팅예제 두개의이름있는파이프를이용한채팅프로그램 fifo 이름과 open 순서유의 select 이용법주목 lily.out cara.out 6
lily.c (1) 1 /*******************************************************************************/ 2 /* lily.c */ 3 /* fifo 이름과 open 하는순서에유의할것! */ 4 /*******************************************************************************/ 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <errno.h> 8 #include <unistd.h> 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <fcntl.h> 12 #include <string.h> 13 14 #define MSG_SIZE 1024 15 7
lily.c (2) 16 int main(int argc, char *argv[]) 17 { 18 char *fifo1 = "lily2cara"; 19 char *fifo2 = "cara2lily"; 20 char snd_buf[msg_size]; 21 char rcv_buf[msg_size]; 22 char msg_header[msg_size]; 23 int hd_length; 24 int rfd; 25 int sfd; 26 fd_set master_set, read_set; 27 int rd_cnt; 28 if (argc!= 2) { 29 printf("usage: %s user_name \n", argv[0]); 30 exit(1); 31 } 32 memset(msg_header, 0, MSG_SIZE); 33 sprintf(msg_header, "%s : ", argv[1]); 34 hd_length = strlen(msg_header); 8
lily.c (3) 35 if (mkfifo(fifo1, 0666) == -1) { 36 if (errno!= EEXIST) { 37 fprintf(stderr, "Error in mkfifo %s\n", fifo1); 38 exit(2); 39 } 40 } 41 if (mkfifo(fifo2, 0666) == -1) { 42 if (errno!= EEXIST) { 43 fprintf(stderr, "Error in mkfifo %s\n", fifo2); 44 exit(3); 45 } 46 } 47 if ((rfd = open(fifo2, O_RDONLY)) < 0) { 48 fprintf(stderr, "Error in open %s\n", fifo2); 49 exit(4); 50 } 51 if ((sfd = open(fifo1, O_WRONLY)) < 0) { // blocking until open for reading 52 fprintf(stderr, "Error in open %s\n", fifo1); 53 exit(5); 54 } 9
lily.c (4) 55 FD_ZERO(&master_set); 56 FD_SET(0, &master_set); 57 FD_SET(rfd, &master_set); 58 while (read_set = master_set, select(sfd+1, &read_set, NULL, NULL, NULL) > 0) { // sfd + 1 59 if (FD_ISSET(0, &read_set)) { 60 if ((rd_cnt = read(0, snd_buf, MSG_SIZE)) <= 0) { 61 fprintf(stderr, "Error in read from stdin\n"); 62 exit(6); 63 } 64 else { 65 if (write(sfd, msg_header, hd_length)!= hd_length) { 66 fprintf(stderr, "Error in write to fifo\n"); 67 exit(7); 68 } 69 if (write(sfd, snd_buf, rd_cnt)!= rd_cnt) { 70 fprintf(stderr, "Error in write to fifo\n"); 71 exit(7); 72 } 73 } 74 } 10
lily.c (5) 75 if (FD_ISSET(rfd, &read_set)) { 76 if ((rd_cnt = read(rfd, rcv_buf, MSG_SIZE)) <= 0) { 77 fprintf(stderr, "Error in read from fifo\n"); 78 exit(8); 79 } 80 else { 81 if (write(1, rcv_buf, rd_cnt)!= rd_cnt) { 82 fprintf(stderr, "Error in write to stdout\n"); 83 exit(9); 84 } 85 } 86 } 87 } 88 } 11
cara.c (1) 1 /*******************************************************************************/ 2 /* cara.c */ 3 /* fifo 이름과 open 하는순서에유의할것! */ 4 /*******************************************************************************/ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <errno.h> 9 #include <unistd.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <string.h> 14 15 #define MSG_SIZE 1024 16 12
cara.c (2) 17 int main(int argc, char *argv[]) 18 { 19 char *fifo1 = "lily2cara"; 20 char *fifo2 = "cara2lily"; 21 char snd_buf[msg_size]; 22 char rcv_buf[msg_size]; 23 char msg_header[msg_size]; 24 int hd_length; 25 int rfd; 26 int sfd; 27 fd_set master_set, read_set; 28 int rd_cnt; 29 if (argc!= 2) { 30 printf("usage: %s user_name \n", argv[0]); 31 exit(1); 32 } 33 memset(msg_header, 0, MSG_SIZE); 34 sprintf(msg_header, "%s : ", argv[1]); 35 hd_length = strlen(msg_header); 13
cara.c (3) 36 if (mkfifo(fifo1, 0666) == -1) { 37 if (errno!= EEXIST) { 38 fprintf(stderr, "Error in mkfifo %s\n", fifo1); 39 exit(2); 40 } 41 } 42 if (mkfifo(fifo2, 0666) == -1) { 43 if (errno!= EEXIST) { 44 fprintf(stderr, "Error in mkfifo %s\n", fifo2); 45 exit(3); 46 } 47 } 48 if ((sfd = open(fifo2, O_WRONLY)) < 0) { // blocking until open for reading 49 fprintf(stderr, "Error in open %s\n", fifo2); 50 exit(5); 51 } 52 if ((rfd = open(fifo1, O_RDONLY)) < 0) { 53 fprintf(stderr, "Error in open %s\n", fifo1); 54 exit(4); 55 } 14
cara.c (4) 56 FD_ZERO(&master_set); 57 FD_SET(0, &master_set); 58 FD_SET(rfd, &master_set); 59 while (read_set = master_set, select(rfd+1, &read_set, NULL, NULL, NULL) > 0) { // rfd + 1 60 if (FD_ISSET(0, &read_set)) { 61 if ((rd_cnt = read(0, snd_buf, MSG_SIZE)) <= 0) { 62 fprintf(stderr, "Error in read from stdin\n"); 63 exit(6); 64 } 65 else { 66 if (write(sfd, msg_header, hd_length)!= hd_length) { 67 fprintf(stderr, "Error in write to fifo\n"); 68 exit(7); 69 } 70 if (write(sfd, snd_buf, rd_cnt)!= rd_cnt) { 71 fprintf(stderr, "Error in write to fifo\n"); 72 exit(7); 73 } 74 } 75 } 15
cara.c (5) 76 if (FD_ISSET(rfd, &read_set)) { 77 if ((rd_cnt = read(rfd, rcv_buf, MSG_SIZE)) <= 0) { 78 fprintf(stderr, "Error in read from fifo\n"); 79 exit(8); 80 } 81 else { 82 if (write(1, rcv_buf, rd_cnt)!= rd_cnt) { 83 fprintf(stderr, "Error in write to stdout\n"); 84 exit(9); 85 } 86 } 87 } 88 } 89 } 16
기본실습 #2 파이프를이용한채팅예제 (100 점 ) 17
select 응용과 pselect select 를이용한이식성 / 정밀도높은 sleep 구현 마이크로초단위 sleep 구현 struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 500; select (0, NULL, NULL, NULL, &tv); pselect (POSIX select) man 으로 3가지차이점확인 timeval 대신 timespec 구조체사용으로나노초단위타이머이용 timout 값유지 재설정필요없음 sigmask로시그널차단 18
응용과제 1 (1) 서버를통한 1:1 채팅 (1000 점 ) 내용채팅 서버는공통 FIFO( 이름을미리정할것!) 생성후대기 채팅클라이언트는자신이데이터를받을 FIFO 생성후, 공통 FIFO 에접속하여자신의아이디 (FIFO 이름 ) 전달 채팅서버는전달된아이디로개별 FIFO 를열고, 공통 FIFO 감시 채팅클라이언트는모든메시지앞에자신의이름을붙여공통 FIFO 로전달 채팅서버는공통 FIFO 로메시지가들어오면그메시지를상대방 FIFO 로전달 채팅클라이언트는사용자의키보드입력이들어오면아이디를붙여공통 FIFO 로보내고, 자신의 FIFO 에데이터가들어오면화면에출력 19
응용과제 1 (2) 응용과제 #1 체크리스트 ( 서버 ) 1. FIFO 정상생성 체크리스트 완성여부 (O, X) 비고 ( 서버 ) 2. 클라이언트정상접속 (id 확인후, 개별 FIFO open ) 서버화면에상태메시지출력 ( 서버 ) 3. 클라이언트메시지수신 ( 서버 ) 4. 상대방클라이언트에게메시지전달 ( 클라이언트 ) 1. FIFO 정상생성 ( 클라이언트 ) 2. 이미알려진서버 FIFO로접속 ( 클라이언트 ) 3. 키보드입력과개별 FIFO 감시하여처리 ( 채팅 ) 두클라이언트간대화확인 ( 화면캡처 4 장이내 ) 20
응용과제 1 힌트 (1) client.c 42 if (mkfifo(my_id, 0666) == -1) { 43 fprintf(stderr, "Error in mkfifo %s\n", my_id); 44 exit(2); 45 } 46 47 if ((sfd = open(com_fifo, O_WRONLY)) < 0) { 48 fprintf(stderr, "Error in open %s\n", my_id); 49 exit(3); 50 } 51 52 write(sfd, my_id, ID_SIZE); // first message 53 54 if ((rfd = open(my_id, O_RDONLY)) < 0) { 55 fprintf(stderr, "Error in open %s\n", my_id); 56 exit(4); 57 } 58 59 FD_ZERO(&master_set); 21
응용과제 1 힌트 (2) server.c 33 if (mkfifo(rd_fifo, 0666) == -1) { 34 if (errno!= EEXIST) { 35 fprintf(stderr, "Error in mkfifo %s\n", rd_fifo); 36 exit(1); 37 } 38 } 39 40 if ((rfd = open(rd_fifo, O_RDONLY)) < 0) { 41 fprintf(stderr, "Error in open %s\n", rd_fifo); 42 exit(2); 43 } 44 45 while ((rd_cnt = read(rfd, rcv_buf, MSG_SIZE)) > 0) { 46 write(1, rcv_buf, rd_cnt); 47 48 if (strlen(rcv_buf) <= ID_SIZE) { // new client 49 if ((sfd = open(rcv_buf, O_WRONLY)) < 0) { 22
응용과제 2 (1) 서버를통한다자간채팅 (1000 점 ) 내용 응용과제 1 기능 임의의순간에클라이언트추가가능 ( 클라이언트수제한이 없어야함 ) 서버는메시지를보내온클라이언트를제외한나머지클라이언트에게메시지전달 클라이언트는 SIGINT가들어오면자신이만든 FIFO 제거후종료 23
응용과제 2 (2) 과제제출시서버와클라이언트설계내용, 프로그램소스및 동작에대한설명반드시포함 응용과제 #2 체크리스트 체크리스트 ( 서버 ) 1. 새로운클라이언트확인후리스트에추가 완성여부 (O, X) 비고 ( 서버 ) 2. 메시지를수신했을때송신자를제외한나머지모든클라이언트에게전달하는지여부 ( 클라이언트 ) SIGINT 가들어왔을때자신이만든 FIFO 제거후종료여부 ( 채팅 ) 4 개이상클라이언트간대화확인 ( 윈도우전체화면캡처 4 장이내 ) 24
응용과제 2 (3) 참고도 com abc Client (id=abc) Client (id=def) def Client (id=xyz) xyz 25
응용과제 3 (1) 서버를통한다자간채팅보완 (1000 점 ) 내용 응용과제 2 기능 임의의순간에클라이언트가종료되더라도나머지 클라이언트간통신은지속할수있도록개선 클라이언트가종료되면해당 FIFO로데이터를보낼수없음 이때발생하는시그널을이용하거나, 각 FIFO의정상동작여부를확인하는방법, 클라이언트가종료될때반드시서버에게알리도록하는방법등이있음 여러가지방법중하나또는둘이상을구현하여임의의순간에 클라이언트가들어오고나갈수있도록함 단, 클라이언트가하나도없는경우서버는종료해도무방함 26
응용과제 3 (2) 과제제출시서버와클라이언트설계내용, 프로그램소스및 동작에대한설명반드시포함 응용과제 #3 체크리스트 체크리스트 완성여부 (O, X) 비고 ( 서버 ) 1. 임의의순간에클라이언트추가, 삭제가능여부 ( 서버 ) 2. 클라이언트가모두나갈때까지정상동작여부 ( 채팅 ) 4 개이상클라이언트간대화및임의의순간에추가, 탈퇴확인 ( 윈도우전체화면캡처 8 장이내 ) 27