(윤성우 저, 'TCP/IP 소켓 프로그래밍' - 11장 관련 내용입니다)
책에 기술 된 Multiplexing 서버와 Multiprocessing 클라이언트를 일부 수정하여 채팅 프로그램을 작성하는 문제입니다.
다중 접속을 지원하고 다중 접속 시 카카오톡 단체채팅방처럼 한 사람의 대화를 모든 사람이 보도록 구현하였습니다.
아래는 Multiplexing 서버를 수정한 채팅 서버 코드입니다.
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 112 113 114 115 | #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[]) { //소켓, timeout값, fd_set값 등을 저장할 변수 선언 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])); //bind 함수를 통해 소켓에 서버의 주소정보를 할당 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_set형 변수 reads의 모든 비트를 0으로 초기화 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; } //Timeout이 발생했다면 if(fd_num==0) { continue; } //변화가 발생했다면 for(i=0; i<fd_max+1; i++) { if(FD_ISSET(i, &cpy_reads)) { // 연결요청이 발생했다면 if(i==serv_sock) { //연결요청 수락 adr_sz=sizeof(clnt_adr); clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz); //해당 연결을 감시하도록 FD_SET을 통해 설정 FD_SET(clnt_sock, &reads); //연결을 통해 clnt_sock 값이 증가했다면 fd_max 재설정 //(연결종료 된 클라이언트 < 새로 연결된 클라이언트)이라면 if(fd_max<clnt_sock) fd_max=clnt_sock; printf("connected client: %d \n", clnt_sock); } else { //메세지를 읽어들임 str_len=read(i, buf, BUF_SIZE); //종료 요청이 왔다면 연결 종료, fd_set값 초기화, 해당 소켓 종료 if(str_len==0) { FD_CLR(i, &reads); close(i); printf("closed client: %d \n", i); } //메세지가 왔다면 else { //write(i, buf, str_len); // echo! //메세지를 모든 사람에게 전송 (4가 첫번째 소켓) for(int j=4; j < fd_max + 1; j++) { write(j, buf, str_len); } } } } } } close(serv_sock); return 0; } void error_handling(char *buf) { fputs(buf, stderr); fputc('\n', stderr); exit(1); } |
아래는 Multiprocessing 클라이언트를 수정한 채팅 클라이언트 코드입니다.
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 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 100 #define NAME_SIZE 20 void error_handling(char *message); void read_routine(int sock, char *buf); void write_routine(int sock, char *buf); char name[NAME_SIZE] = "[NULL]"; char buf[BUF_SIZE]; int main(int argc, char *argv[]) { //소켓, pid값 등을 저장할 변수선언 int sock; pid_t pid; char buf[BUF_SIZE]; struct sockaddr_in serv_adr; //입력 포맷이 맞지 않으면 오류 출력 후 종료 if(argc!=4) { printf("Usage : %s <IP> <port> <name>\n", argv[0]); exit(1); } //데이터를 형식에 맞추어 쓰드록 지정 sprintf(name, "[%s]", argv[3]); //클라이언트 소켓 설정 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])); //connect함수를 통해 서버와 연결 시도 if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("connect() error!"); //fork 함수를 통해 입력, 출력을 처리하는 프로세스를 각각 만듦 pid=fork(); if(pid==0) write_routine(sock, buf); else read_routine(sock, buf); close(sock); return 0; } //서버로부터 메세지를 읽어들이는 함수 void read_routine(int sock, char *buf) { char total_msg[NAME_SIZE + BUF_SIZE]; while(1) { //서버로부터 메세지를 읽어들임 int str_len=read(sock, total_msg, NAME_SIZE + BUF_SIZE); //close를 받았다면 종료 if(str_len==0) return; //데이터 출력 total_msg[str_len]=0; fputs(total_msg, stdout); //printf("Message from server: %s", buf); } } //서버로 메세지를 전송하는 함수 void write_routine(int sock, char *buf) { char total_msg[NAME_SIZE + BUF_SIZE]; while(1) { //입력값을 받아들임 fgets(buf, BUF_SIZE, stdin); //만약 'q'나 'Q' 문자가 입력되면 종료 if(!strcmp(buf,"q\n") || !strcmp(buf,"Q\n")) { shutdown(sock, SHUT_WR); return; } //데이터를 형식에 맞추어 쓰드록 지정 sprintf(total_msg, "%s %s", name, buf); //서버로 입력한 데이터 전송 write(sock, total_msg, strlen(total_msg)); } } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } |
'학교 수업 및 과제 > 컴퓨터 네트워크' 카테고리의 다른 글
2. Select 함수를 사용하는 I/O 멀티플렉싱 에코 클라이언트 작성 (0) | 2018.05.28 |
---|---|
1. Iterative Echo Server 만들기 (0) | 2018.05.19 |