본문 바로가기
코스웨어/15년 스마트컨트롤러

20150827-22번-윤재희-일일업무일지 - Select 함수를 사용한 채팅프로그램(완성)

by 알 수 없는 사용자 2015. 8. 27.
728x90
반응형

==================================Outline====================================

Select 함수를 사용한 채팅프로그램

초과 접속자 발생 시 차단

- ctrl + d 입력 시 정상종료

포트 설정

- ctrl + c 정상종료

----------------------------------------------------------------------------

초과 접속자 발생 시 차단

 

지난 시간에 이어...

 

접속자 제한을 3으로 하자서버에 접속자가 초과 된 상황을 만들기 위한 작업이다.

 

** 코딩내용을 수정할 때 우선 코딩의 전체 흐름을 살펴본다.

수정한 코드가 전체 프로그램에 미치는 영향을 파악한 후 작업을 진행해야 한다.

눈에 실핏줄 터지기 싫으면...

 

smartsock.h에서 MAX_USER 값을 30에서 3으로 줄인다.

 

 

접속 제한 숫자를 초과하는 클라이언트의 접속을 막아야 한다하지만 영문도 모르고 접속이 안되는 접속자의 속상함 방지를 위해일단 접속은 허가한 후 넌 팅길거라는 메시지를 보내준다초과 접속자의 접속 처리를 위해 접속자의 대화 소켓을 담는 icSock변수를 icSock[MAX_USER+1]로 처리해준다.

 

 

 

accept후 조건문을 만들어 uiUser의 크기가 MAX_USER와 크거나 같을 때 사용자의 접속을 끊고 안내 메시지를 보내준다.

 

 

컴파일 후 클라이언트를 접속시키면 서버와 제한 숫자 내 클라이언트는 정상 작동하지만초과 접속한 클라이언트에게 무한반복으로 버퍼에 있는 메시지가 출력된다초과인원이 출입 시 동작하는 if문으로 간다초과 접속된 클라이언트에게 종료 메시지를 보내주고 클라이언트에서는 해당 메시지를 받으면 접속이 끊어지도록 코딩하자.

 

 

<server.c>

 

  

 

<client.c>

 

** 캡쳐된 코드 소속이 궁금할 때 구분법: iFd쓰면 client, icSock/iSock쓰면 server

 

 

Ctrl + d 입력 시 정상종료

 

서버에서 Ctrl + D를 누르면 모든 클라이언트에게 종료 메시지를 출력하고 서버를 종료하도록 처리해주자.

 

 

 

<client.c>

 

 

 

포트 설정

 

서버or클라이언트 실행 시 포트번호를 설정할 수 있도록 해주자.

우선 실행파일이 인자를 받을 수 있도록 메인함수를 고쳐준다서버부터.

 

 

실행 시 포트번호를 인자로 받게 되고 이것이 유효한 숫자일 경우 포트번호를 변경해준다.

 

 

 

bind를 위해 접속정보를 세팅하는 구조체로 가서 PORT번호를 수정해준다.

 

 

클라이언트에서도 똑같이 작업해준다.

 

 

 

Ctrl + c 정상종료

 

서버나 클라이언트에서 정상종료가 아닌 ctrl+c를 눌렀을 경우에도 클라이언트가 고통 받지 않고 정상종료 하도록 코딩해주자.

 

ctrl+c로 눌렀을 경우대화소켓으로 받은 read의 반환 값은 0이 된다이를 활용하여 read값이 0일 경우 정상종료 시켜주자.

 

<server.c>

 

 

 

<client.c>

 

 

채팅 서버클라이언트 만들기는 여기서 끝낸다지금까지 코딩한 채팅 프로그램이 성에 안차면 멀티룸 채팅에 도전해 보시씨요.

 

** 멀티룸 채팅 채팅 방을 여러 개 만들어서 유저가 선택적으로 접속


/*** 소스 ***/


#include "smartsock.h"

int main(int iRtn, char *cpCMD[])
{
  int iFd;
  struct sockaddr_in stAddr;
  int iLen;
  char cBuf[BUF_SIZE];  
  fd_set fdRead;
  char cMSG[MSG_SIZE];
  char cNick[NIC_NAME_SIZE];
  unsigned short usPORT=PORT; //PORT == 7777

  if(iRtn == 2)
  {
    iFd  = atoi(cpCMD[1]);
    if(1024 < iFd)
    {
      if(65535 > iFd)
      {
        usPORT=iFd;        
        printf("PORT no. %d\n", usPORT);
      }
    }
  }


  /*** Nick Name in ***/
  while(1)
  {
    printf("Please Input Nick Name\n");
    fflush(stdout);
    iRtn = read(0, cNick, NIC_NAME_SIZE);
    if(iRtn < 2)//그냥 엔터키를 눌렀을 경우. **ctl + d 누르면 iRtn = 0
      continue;

    cNick[iRtn-1= 0;
    break;

  }
  /*** socket ***/

  iFd = socket(AF_INET, SOCK_STREAM, 0);
  if(-1 == iFd)
  {
    perror("socket:");
    return 100;
  }

  /*** structure setting ***/
  stAddr.sin_family = AF_INET;
  stAddr.sin_addr.s_addr = inet_addr(IP);
  stAddr.sin_port = htons(usPORT);

  iLen = sizeof(struct sockaddr_in);

  /*** connect ***/
  iRtn = connect(iFd, (struct sockaddr *)&stAddr, iLen);
  if(-1 == iRtn)
  {
    perror("connect:");
    close(iFd);
    return 200;
  }

  write(iFd, cNick, NIC_NAME_SIZE);

  while(1)
  {
    FD_ZERO(&fdRead);  
    FD_SET(0&fdRead);
    FD_SET(iFd, &fdRead);      
    select(iFd+1,&fdRead, 000);

    if(0 != (FD_ISSET(0&fdRead) ) )
    {
      iRtn = read(0, cBuf, BUF_SIZE);      
      if(iRtn == 0)
      {
        write(iFd, MSG_END, sizeof(MSG_END));
        break;
      }
      cBuf[iRtn - 1= 0;
      sprintf(cMSG, "[%s]%s ", cNick, cBuf);
      write(iFd, cMSG, MSG_SIZE);

      printf("[Send MSG]: [%s]\n", cBuf);    
    }
    if(0 != (FD_ISSET(iFd, &fdRead) ))
    {
      iRtn = read(iFd, cMSG, sizeof(cMSG));      
      if(iRtn == 0)
      {
        printf("Server does not respond\n");
        break;
      }
      if0 == strncmp(cMSG, MSG_END, sizeof(MSG_END) ))
      {
        break;

      }
      printf("[%s]\n", cMSG);    
    }
  }



  /*** read & write ***/
  //memset(cBuf, 0, BUF_SIZE);
  //iRtn = read(0, cBuf, BUF_SIZE);

  close(iFd);
  return 0;
}

<client.c>


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

int main(int iRtn, char *cpCMD[])
{
  int iSock;   //소켓 함수의 반환 값을 받는 변수
  int icSock[MAX_USER+1];   //accept의 반환 값을 받는 변수
  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
  char cNick[MAX_USER][NIC_NAME_SIZE];
  char cMSG[MSG_SIZE];
  unsigned short usPORT=PORT;  //PORT == 7777

  if(iRtn == 2)
  {
    iSock  = atoi(cpCMD[1]);
    if(1024 < iSock)
    {
      if(65535 > iSock)
      {
        usPORT = iSock;
        printf("PORT no. %d\n", usPORT);
      }
    }
  }

  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(usPORT);

  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);
    FD_SET(0&fdRead);
    FD_SET(iSock, &fdRead);
    iMSock = iSock;    

    for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)  //사용자가 접속하면 해당 Fd를 set해주고 가장 높은 fd값을 저장
    {
      FD_SET(icSock[uiCnt], &fdRead);
      //FD_SET(iSock, &fdRead);
      if(iMSock < icSock[uiCnt])
      {
        iMSock = icSock[uiCnt];
      }      
    }
    select((iMSock+1), &fdRead, 000);//select 함수를 사용하여 감시해준다.(입력이 있을때까지 무한대기)
    if0 != FD_ISSET(iSock, &fdRead) ) //지정된 소켓 번호가 있으면 대화 소켓 번호를 받아저장하고 user+
    {
      icSock[uiUser] = accept(iSock, (struct sockaddr *)&stAddr, &uiSockLen); //접속자의 정보가 stAddr에 입력된다.
      if(icSock[uiUser] < 0)
      {
        perror("Accept : ");
        continue;
      }
      if(uiUser >= MAX_USER)
      {
        read(icSock[uiUser], cBuf, sizeof(cBuf));
        sprintf(cMSG, "Server is not vacant");
        write(icSock[uiUser], cMSG, sizeof(cMSG));
        write(icSock[uiUser], MSG_END, sizeof(MSG_END));
        close(icSock[uiUser]);

        printf("Server is not vacant [%s]\n", cBuf);
        printf("Client iP :%s\n", inet_ntoa(stAddr.sin_addr));
        continue;
      }
      read(icSock[uiUser], cNick[uiUser], NIC_NAME_SIZE);
      printf("Incoming Client :[%s] \n", cNick[uiUser]);
      printf("Client IP :%s\n", inet_ntoa(stAddr.sin_addr));
      write(icSock[uiUser], "Welcome :)"sizeof("Welcome :)"));
      sprintf(cMSG, "[%s]님이 입장하셨습니다.",cNick[uiUser] );
      for(uiCnt2 = 0; uiCnt2 < uiUser; ++uiCnt2)
      {
        write(icSock[uiCnt2] ,cMSG, MSG_SIZE);
      }
      ++uiUser;

    }
    if(0 != FD_ISSET(0&fdRead))    //서버에서 키보드 입력 받은 내용 클라이언트에게 보내기
    {
      iRtn = read(0, cBuf, BUF_SIZE);
      if(iRtn == 0)
      {
        for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)
        {
          sprintf(cMSG, "Server is aborted");
          write(icSock[uiCnt], cMSG, sizeof(cMSG));
          write(icSock[uiCnt], MSG_END, sizeof(MSG_END));
        }
        break;
      }
      sprintf(cMSG, "[공지사항]:[%s]", cBuf);
      for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)
      {
        write(icSock[uiCnt], cMSG, MSG_SIZE);//모든 사용자에게 보낸다.
      }
    }
    for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)
    {  
      if0 != FD_ISSET(icSock[uiCnt], &fdRead))
      {
        iRtn = read(icSock[uiCnt], cMSG, MSG_SIZE);
        
        if(
        ( 0 == strncmp(cMSG, MSG_END, sizeof(MSG_END) )) 
        ||
        0 == iRtn)
        {
          sprintf(cMSG, "[%s]님이 퇴장하셨습니다.", cNick[uiCnt]);
          close(icSock[uiCnt]);
          --uiUser;
          icSock[uiCnt] = icSock[uiUser];          
          memcpy(cNick[uiCnt], cNick[uiUser], sizeof(NIC_NAME_SIZE));  
        }
        for(uiCnt2 = 0; uiCnt2 < uiUser; ++uiCnt2)
        {
          write(icSock[uiCnt2] ,cMSG, MSG_SIZE);
        }
      }
    }

  }
  for(uiCnt = 0; uiCnt < uiUser; ++uiCnt)
  {
    close(icSock[uiCnt]);  
  }
  close(iSock);
  return 0;
}


<server.c>


#ifndef __SMARTSOCK_H__
#include <sys/select.h>
#define __SMARTSOCK_H__

#include <stdio.h>
#include <string.h>
#include <strings.h>
// socket & bind & listen & accept & connect
#include <sys/types.h>
#include <sys/socket.h>

// sockaddr_in
#include <netinet/in.h>

// read & write
#include <unistd.h>

// htonl
#include <arpa/inet.h>

// errno, perror
#include <errno.h>

// open
#include <fcntl.h>
#include <sys/stat.h>

//select
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>

#define PORT      7777
#define IP        "192.168.0.173"

#define MAX_USER     3
#define NIC_NAME_SIZE   9
#define NIC_NAME_MSG   (9+2)

#define BUF_SIZE  255              //only message
#define MSG_SIZE  (BUF_SIZE+1+NIC_NAME_MSG)  //message + Nick Name
#define MSG_END    "\x01\x02\x03"        



#endif /* __SMARTSOCK_H__ */


<smartsock.h>


/*** 파일 ***/


client.c


server.c


smartsock.h







728x90