170 likes | 987 Views
인턴사원 주간 OJT 실습발표 ( 간단한 메신저의 구현 / 로깅 ). SW 팀 김정섭. 발 표 목 록 메신저 프로그램 구현 - 프로세스간 통신구축과정 - 메시지 송 / 수신부 - 대화내역 로깅 SVN, Makefile 결 론 보완사항 / 교훈. 메신저 프로그램 구현시 염두해두어야할 사항 프로세스간 통신환경의 구축 과정은 어떻게 진행되는가 ? 메신저 프로그램은 수신대기와 송신준비를 동시에 이루어지는데 이것을 어떻게 구현할 것인가 ?
E N D
인턴사원 주간 OJT 실습발표(간단한 메신저의 구현 / 로깅) SW팀김정섭
발 표 목 록 메신저 프로그램 구현 - 프로세스간 통신구축과정 - 메시지송/수신부 - 대화내역 로깅 SVN, Makefile 결 론 보완사항 / 교훈
메신저 프로그램 구현시염두해두어야할 사항 프로세스간 통신환경의 구축 과정은 어떻게 진행되는가? 메신저 프로그램은 수신대기와 송신준비를 동시에 이루어지는데 이것을 어떻게 구현할 것인가? 주고받은 메시지를 어떤 방식으로 기록할것인가?
프로세스간 통신환경의 구축 과정은 어떻게 진행되는가? TCP/IP 통신 함수들을 차례대로 호출하여 서버가 클라이언트와의 통신을 준비한다. Socket() 통신을 위한 소켓 을 생성하여 반환한다. bind() 를 이용하여 socket에 server socket 에 필요한 정보를 할당하고 listen() // 클라이언트 접속 요청을 확인 connect() from client 클라이언트로부터 connent()가 들어오게 되면 accept() 후에새로운소켓이 생성되며 서버와 클라이언트간 통신 시작 close()
프로세스간 통신환경 구축 과정 . /*TCP/IP를 사용하기위해 소켓을 생성한다. SOCK_STREAM*/ if ((servSockfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1) // 소켓생성 / 실패대응 { perror("socket failed"); exit(1); } /*socket에 주소와 port 를 할당하기 위해 sockaddr_in구조체에 접근.*/ memset(&servAddr, 0, sizeof(servAddr)); // 메모리 주소 초기화 servAddr.sin_family = AF_INET; // IP버전 4에서 사용하는 인터넷 통신 프로토콜 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //시스템 마다 IP 가 다를 것이므로 주소 지정을 고정 IP로 하지 않음 servAddr.sin_port = htons(PORT); // 포트번호 적용 /*bind() 함수를 이용하여 socket에 server socket 에 필요한 정보를 할당하고 커널에등록*/ if(bind(servSockfd, (structsockaddr*) &servAddr, sizeof(servAddr))== -1) // bind()실행 / 실패대응 { perror("bind failed"); exit(1); } /*listen() 함수로 클라이언트 접속 요청을 확인 */if(listen(servSockfd, MAXPENDING)== -1) // listen()실행 / 실패대응 { perror("listen failed"); exit(1); } clntLen = sizeof(clntAddr); //클라이언트에게서 connect()가 들어오면 accept()가 활성화되어 통신이 시작된다. if((clntSockfd = accept(servSockfd,(structsockaddr*)&clntAddr, &clntLen))== -1) { perror("accept failed"); exit(1); }
메신저 프로그램은 수신대기와 송신대기를 동시에 이루어지는데 • 이것을 어떻게 구현할 것인가? • fork()를사용하여 자식프로세스를 수신모드 • 부모 프로세스를 송신모드로 놓는다.
메세지 수신 / 로깅 사용된 개념 : fork() 수신대기와 전송준비가 동시에 이루어져야 한다. switch(pid=fork()) //포크 생성 부모에게는 새로 생성된 자식 프로세스 PID가 반환되며, 자식 프로세스에는 0이 반환 { case -1: // 에러 대응 perror("fork failed"); exit(1); case 0: //자식 프로세스 == 수신모드 if(fd>0) //open()이 정상으로 실행되면 { write(fd,"\n_____accept!_____\n",strlen("\n_____accept!_____\n")); } //연결을 알리는 기록을 로그파일에 기록한다. close(servSockfd); //accept로 새로운 소켓이 생성되므로 서버에의한 소켓을 닫아준다. signal(SIGINT,signalHandler); //send(부모)에서 quit을 입력하면 kill()로부터 SIGINT를 전송받아signalHandler()실행 while(1) //SIGINT가 들어오지 않는한 계속해서 수신대기모드 실행 { if((recvLen=recv(clntSockfd, recvBuffer,BUFSIZE,0))== -1) //recv()실행 / 수신 실패 대비 { perror("recv failed"); exit(1); } if(recvLen ==0) //받은 바이트수가 0이다 == 클라이언트가 네트웍을 끊는 경우, recv가 0바이트를 수신 break; //루프 recvBuffer[recvLen]='\0'; //수신이 제대로 들어왔다면 수신메세지 버퍼 마지막칸에널을넣어주고 printf("client_said :%s",recvBuffer); // 수신메세지를 화면에 출력해준 후 write(fd,recvBuffer,strlen(recvBuffer)); // 수신메시지를 로그파일에 기록하고 다시 처음부터 } // 루프를 빠져나가게 되면 소켓을 닫고 프로그램 종료
부모 프로세스 (송신모드) Switch(pid=fork()) . . . default: //부모프로세스 == 송신모드 while(1) { fgets(sendBuffer,BUFSIZE,stdin); // 보내고싶은 메시지를 입력 받는다. if(!strncmp(sendBuffer,"quit",4)) //만약 quit를 입력 받았다면 { kill(pid,SIGINT); // kill pid(자식프로세스)에게 SIGINT(프로세스 인터럽트) 시그널전송 시그널 핸들러 함수 break; //부모프로세스도 루프로부터 추방 후 종료당함 } if(send(clntSockfd,sendBuffer,strlen(sendBuffer),MSG_NOSIGNAL) != strlen(sendBuffer)) // send()실행과 동시에 반환값(버퍼 바이트수)과 실제 버퍼 바이트수가 일치하는지 검사 = send 성공 검사 { if(errno == EPIPE) // 파이프 브로큰시 { printf("client is out\n"); break; //루프로부터 추방후종료당함 } perror("send failed"); exit(1); } write(fd,sendBuffer,strlen(sendBuffer)); //if검문을 통과하면 send()와 송신내역 기록이 무사히 수행 }
주고받은 메시지를어떤방법으로기록할것인가? open() 으로 기록을 위한 파일을 생성한후 송수신 프로세스 안에 write()를 포함하게하여 실시간으로 메시지가 기록되게 한다.
Accept() 수행 후 open() 으로 기록을 위한 파일을 생성한 후. case 0: if(fd>0) { write(fd,"\n_____accept!_____\n",strlen("\n_____accept!_____\n")); } close(servSockfd); signal(SIGINT,signalHandler); while(1) { if((recvLen=recv(clntSockfd, recvBuffer,BUFSIZE,0))== -1) { perror("recv failed"); exit(1); } if(recvLen ==0) break; recvBuffer[recvLen]='\0'; printf("client_said :%s",recvBuffer); write(fd,recvBuffer,strlen(recvBuffer)); } 수신 프로세스 송신 프로세스 default: while(1) { fgets(sendBuffer,BUFSIZE,stdin); if(!strncmp(sendBuffer,"quit",4)) { kill(pid,SIGINT); break; } if(send(clntSockfd,sendBuffer,strlen(sendBuffer),MSG_NOSIGNAL) != strlen(sendBuffer)) { if(errno == EPIPE) { printf("client is out\n"); break; } perror("send failed"); exit(1); } write(fd,sendBuffer,strlen(sendBuffer)); } 메시지가 송신, 수신될때마다write()가 계속 실행되게 한다. 송수신 루프 탈출시close()로 열려진 파일을 닫아준다.
프로그램 동작확인 대화화면 대화내역 기록
SVN서브버전(Subversion) 개 요 하나의 프로젝트를 여러 인원이 통합하여 관리 / 업데이트 해야 하는경우 지속적으로 수정되는 소스에 대한 정보(수정한 사람, 수정날짜, 수정 내역)와복구기능 등이 필요하다. 이러한 기능을 제공하는 툴을 SVN이라 한다. 실습 방법 TortoiseSVN을 윈도우OS 컴퓨터에 설치하여 프로젝트 수행간 이용 문제점 - 리눅스컴퓨터에서는 활용하지 않음 - 프로젝트 수행간 SVN을 적극적으로 활용하지 못했다.
Makefile 개요 프로그램의 기능과 코드등이복잡해지는경우 여러 개의 파일로 나누어 개발하게 되는데 실행파일을 생성하기 위해 컴파일의 순서 처리과정을 담은 리눅스 명령어를 포함하는 파일 실습방법 새롭게 만들고자 하는 파일명(타겟리스트) 종속파일의 존재가 타겟리스트생성을위한 전제조건이다 tab 타겟 리스트를 만들기 위해 실행해야 하는 명령어 라인
결 론 SVN을 통한 소스 버전관리시스템 경험 TCP/IP 프로세스간 통신 구축 수행 결과물을 Makefile로 컴파일설정을 해주는 과정 프로젝트 수행동안여러가지라이브러리 함수 습득과 사용
보완사항/ 교 훈 보완사항 소규모 프로젝트라 Makefile을 제대로 느껴보지 못함 SVN의 적극적인 활용 교 훈 상투적으로 나오는 알고리즘을 따로 정리하자 함수로 시작해서 함수로 끝난 프로젝트 변수에 대한 반환값에 집착해야… 시행착오 리스트를 따로 만들어 보관