Busan IT/공장내 Network2015. 8. 26. 10:09

==================================Outline====================================
Select함수를 사용한 채팅 서버
----------------------------------------------------------------------------



/*** server.c ***/

#include "smartsock.h"
#include <unistd.h>

int main(void)
 {
   int iSock;   //소켓 함수의 반환 값을 받는 변수
  int icSock[MAX_USER];   //accept의 반환 값을 받는 변수
  int iRtn;
   struct sockaddr_in stAddr;
   socklen_t uiSockLen=sizeof(struct sockaddr);
   char cBuf[BUF_SIZE];
   const char * cP;
   fd_set fdRead;
   unsigned int uiUser;
   unsigned int uiCnt, uiCnt2;
   int iMSock;  //store greatest number in file descriptors


   iSock = socket(AF_INET, SOCK_STREAM, 0);    //AF_INET = 2, 
   if(0 > iSock)
   {
     perror("socket : ");
     return -1;
   }
   // stAddr구조체에 socket연결을 위한 필수 정보 입력  setting
   bzero(&stAddr, sizeof(stAddr));            //구조체 비우기(0으로 채우기)
   stAddr.sin_family = AF_INET;               //#define AF_INET 2 /* IP protocol family. */
   stAddr.sin_addr.s_addr = inet_addr(IP);    //IP와 PORT값은 헤더파일에 정의되어 있다.
   stAddr.sin_port = htons((PORT));

   iRtn = bind(iSock, (struct sockaddr *)&stAddr,sizeof(stAddr));
   if(iRtn < 0)
   {
     perror("bind : ");
     close(iSock);

     return -2;
   }
   iRtn = listen(iSock, 5);
   if(iRtn != 0)
   {
     perror("listen : ");
     close(iSock);

     return -3;
   }
   uiUser = 0;
   while(1
   {
     // setting fd_set
     FD_ZERO(&fdRead);    //fdRead 변수 1024byte 전부 0으로 세팅 
     FD_SET(0&fdRead);  //키보드 입력 감시
     FD_SET(iSock, &fdRead);  //접속 소켓 감시
     iMSock = iSock;      //소켓의 최대값 저장 -> select함수 첫번째 인자는 감시하는 파일 값 + 1기 때문이 이에 대한 최대값 저장

     for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)  //사용자가 접속하면 해당 Fd를 set해주고 가장 높은 fd값을 저장
    {
       FD_SET(icSock[uiCnt], &fdRead);  //접속한 사용자수만큼의 icSock배열에 소켓 값이 들어있음으로 이를 감시 
       if(iMSock < icSock[uiCnt])
       {
         iMSock = icSock[uiCnt];  //iMSock에 소켓의 최대값 저장
       }      
     }

     select((iMSock+1), &fdRead, 000);//select 함수를 사용하여 감시해준다.(입력이 있을때까지 무한대기)

     if0 != FD_ISSET(iSock, &fdRead) ) //랑데뷰 소켓이 set(1)되어 있으면(사람이 접속하면) accept함수를 받아서 대화 소켓을 열어준다.
     {
       icSock[uiUser] = accept(iSock, (struct sockaddr *)&stAddr, &uiSockLen); //대화 소켓 값을 icSock배열에 저장해준다.
       if(icSock[uiUser] < 0)
       {
         perror("Accept : ");
         continue;
       }
       printf("Incoming Client \n");
       printf("Client IP :%s\n", inet_ntoa(stAddr.sin_addr));      //사용자가 접속하면 IP를 출력시켜준다.
       write(icSock[uiUser], "Welcome :)"sizeof("Welcome :)"));    //접속자에게 "Welcome" 메세지 전송
       ++uiUser;                //사용자 수 +
     }
     if(0 != FD_ISSET(0&fdRead))                //서버에서 입력 값이 있을 경우에
    {  
       iRtn = read(0, cBuf, MSG_SIZE);            //키보드 값을 읽어 cBuf에 저장
       cBuf[iRtn - 1= 0;              //\n\r을 제거하기 위해 문자열 끝에 0을 강제 삽입
       for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)
       {
         write(icSock[uiCnt], cBuf, MSG_SIZE);//모든 사용자에게 보낸다.    //접속한 클라이언트에게 메세지 전송
       }
     }
     for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)          //접속 클라이언트 대화 소켓을 순회하면서 대화내용이 있는지 확인
     {  
       if0 != FD_ISSET(icSock[uiCnt], &fdRead))
       {
         read(icSock[uiCnt], cBuf, MSG_SIZE);          //대화가 있으면 읽어들임
         for(uiCnt2 = 0; uiCnt2 < uiUser; ++uiCnt2)
         {
           if(uiCnt != uiUser)      
             write(icSock[uiCnt2] ,cBuf, MSG_SIZE);        //모든 클라이언트들에게 내용을 전송
         }
         if0 == strncmp(cBuf,MSG_END, strlen(MSG_END)) )       //클라이언트가 종료 메세지를 입력할 경우
         {          
           close(icSock[uiCnt]);            //해당 소켓을 닫고          
           --uiUser;                //uiUser 수를 줄여주고
           icSock[uiCnt] = icSock[uiUser];          //빠져나간 자리에 가장 마지막에 들어온 유저의 소켓 번호를 넣어준다
         }
       }
     }

   }
   for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)          //종료시 열린 소켓을 모두 닫아준다.
   {
     close(icSock[uiCnt]);  
   }
   close(iSock);
   return 0;
 }
   
  


반응형
Posted by newind2000