#include <stdio.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
#define MAXPENDING 5 // 서버에서 받을 허용 인원. #define MAXUSER 2 // 소켓으로 받을 사람수.
int main() { int servSock; // 서버소켓 int clntSock[MAXUSER]; // 클라이언트소켓 2개를 받음. unsigned int ui_User; // 접속자 수 int iMaxSock; // select함수의 첫번째 인자. int tempSock; // 임시 소켓. int iRet; // 저장할 변수 unsigned char uc_buff[500]; // 채팅시에 문자열을 받는 버퍼. struct sockaddr_in echoServAddr; // 신구조체의 서버IP를 가지는 변수. struct sockaddr_in echoClntAddr; // 신구조체의 클라이언트IP를 가지는 변수
unsigned short echoServPort; // Port 번호를 가지는 변수. 2byte 크기. unsigned int clntLen; // 클라이언트 변수 unsigned int iCnt; // for 문 돌리기위함. unsigned int iCnt2; // for 문 돌리기위함. fd_set fsStatus; // fd_set 구조체, select 함수를 쓰기위해서 echoServPort = 9999; // Port 번호 0~65535 범위를 가짐. // Socket 생성 //프로토콜패밀리결정, 소켓의형태(데이타보내는방식,SOCK_STREAM or SOCK_DGRAM)설정, //STREAM인지DGRAM인지에 따라서 IPPROTO_TCP , IPPROTO_UDP 로 정해줌. //신뢰성 : STREAM (데이터의 송수신값을 확인하며 보냄),ex)일반파일 //비신뢰성 : DGRAM(데이터을 확인없이 한번에 보냄),ex)MP3.. servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if( 0 > servSock ) // Error 코드 { printf("socket() failed\n"); return 0; } // 메모리 크기를 특정값으로 초기화 여기서는 0 으로 만들어줌. memset(&echoServAddr, 0, sizeof(echoServAddr)); // #define AF_INET PF_INET 2 로 define 되어있음. 패밀리선택, PF_INET 을 쓴다. echoServAddr.sin_family = AF_INET; // Host to Network long 으로 host의 크기를 Long형(4byte)만큼 // 지정하고 little-Endian -> Big-Endian방식으로 보내줌. // 0 을 넣어주면 Host에서 주소를 랜덤으로 줌, 고정적으로 사용하고 싶을때는 직접 주소값을 넣어줌. echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Host to Network short 으로 host의 크기를 short형(2byte)만큼 // 지정하고 little-Endian -> Big-Endian방식으로 보내줌. // 0 을 넣어주면 Host에서 주소를 랜덤으로 줌, 고정적으로 사용하고 싶을때는 직접 주소값을 넣어줌. echoServAddr.sin_port = htons(echoServPort); // Port는 정해져있으므로 정해줌.
// 구조체 안에 설정한것을 소켓에다가 넣어줌. bind() iRet = bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr));
// if조건문을 위해서 iRet 변수를 지정. if( 0 > iRet ) { printf("bind() failed\n"); close(servSock); // 서버소켓 닫기. return 0; } // listen(서버소켓, 받을 클라이언트 수) // if조건문을 위해서 iRet 변수를 지정. iRet = listen(servSock,MAXPENDING); // Client 연결할때마다 하나씩 새로운 소켓을 얻는데 사용. if( 0 > iRet) { printf("listen() failed\n"); close(servSock); return 0; } // 서버로 들어오는 클라이언트연결에대해서 accept()를 호출하면서 소켓을 만든다. clntLen = sizeof(echoClntAddr); // clntLen로 Client 크기를 받고, clntLen의 주소를 인자로 넘김. // 대기중에 있다가 서버에 접속허용을 받으면 Client 소켓을 만듬. 강제로 대기(블로킹함수)
iMaxSock = servSock+1; // Sock의 제일 큰번호를 의미 , 제일큰번호 + 1 ui_User = 0; // 접속자 수. while(1) { FD_ZERO(&fsStatus); // 파일디스크립트 모두 초기화. FD_SET(0,&fsStatus); // 키보드(stdin:0번)입력 비트를 상태확인 위해 SET으로 바꿈. FD_SET(servSock,&fsStatus); // 서버소켓상태 확인을 위해 SET으로 바꿈. for(iCnt2=ui_User; iCnt2 > 0; --iCnt2) // ui_User 접속상태일때 동작. { FD_SET(clntSock[iCnt2-1],&fsStatus); // 클라이언트소켓상태 확인을 위해 SET으로 바꿈. if(iMaxSock <= clntSock[iCnt2-1]) { iMaxSock = clntSock[iCnt2-1] + 1; } } select(iMaxSock,&fsStatus,0,0,0); // 소켓과키보드,읽어오는상태확인, if( 1 == FD_ISSET(servSock,&fsStatus)) // 랑데뷰소켓감지. { tempSock = accept(servSock, (struct sockaddr *)&echoClntAddr,&clntLen); // 소켓은 2가지가 있는데 랑데뷰소켓, 커뮤니케이션소켓이 있다. // accept 함수에 의해서 랑데뷰소켓이 생성됨.(랑데뷰소켓은 Clinet 의 주소를 들고있음) // 랑데뷰소켓이 Client의 정보를 들고 있다가 if( 0 > tempSock ) // 클라이언트소켓 생성 확인 구문 { printf("accept() failed\n"); continue; } // Network to ASCII, 클라이언트IP를 문자열[(ex)192.211.10.20]로 바꿔서 출력해줌. printf("Handling client IP %s\n", inet_ntoa(echoClntAddr.sin_addr)); // Port 번호를 Network to Host short형 크기로 출력해줌. printf("Handling client PORT %d\n", ntohs(echoClntAddr.sin_port)); if(ui_User >= MAXUSER) // 접속자수 제한. 접속자가 5명이 넘어가면 끊고 새로받음. { close(tempSock); continue; } clntSock[ui_User] = tempSock; ++ui_User; printf("현재 접속자수는 [%d]명 입니다.\n", ui_User); } else if( 1 == FD_ISSET(0,&fsStatus) ) // 키보드 입력을 받으면 if 문 실행. { iRet = read(0, &uc_buff, 500); // 키보드로 버퍼에 저장. for(iCnt=ui_User;iCnt > 0; --iCnt) { // 반복문을 통해서 write(clntSock[iCnt-1],&uc_buff, iRet); // 접속한 사람한테 모두 다 날림. } } else { for(iCnt=ui_User;iCnt > 0; --iCnt) // 접속자 모두에게 순서대로 메시지를 보냄. { // 여러명중에 특정 Client가 보낸 메시지를 각각 화면에 출력함. if( 1 == FD_ISSET(clntSock[iCnt-1],&fsStatus) ) // 커뮤니케이션소켓상태 변화가 생기면 if 문 실행. {// read함수로 접속한 client로 부터 글자를 받음. iRet = read(clntSock[iCnt-1],uc_buff,sizeof(uc_buff)-1); printf("[Client%d: ", iCnt-1);//보낸이 확인. fflush(stdout); // '\n' 비워줌. // uc_buff[iRet] = 0; // '\n'문자를 '\0' 으로 변환 write(1,uc_buff,iRet-1); // 화면에 Client로 부터 온 글자를 출력. printf("]\n");// '\n' 출력, // 여기까지는 모두 서버에 출력됨.
for(iCnt2=ui_User; iCnt2 > 0; --iCnt2) { write(clntSock[iCnt2-1],&uc_buff, iRet); // 접속한 사람한테 모두 다 날림. } // write(clntSock,uc_buff,iRet); // 클라이언트소켓에 uc_buff에 있는 글자를 출력. } } if( 'q' == uc_buff[0] ) // q 가 uc_buff에 차면 종료. { break; // 'q' 를 눌러서 종료. } } }
for(iCnt2=ui_User; iCnt2 > 0; --iCnt2) { close(clntSock[iCnt-2]); // 클라이언트소켓을 닫기. } close(servSock); // 서버소켓을 닫기. return 0; }
|