본문 바로가기
코스웨어/11년 내장형하드웨어

[내장형]이성재_10월25일 일일보고서

by 알 수 없는 사용자 2011. 10. 25.
728x90
반응형
멀티플렉싱에서 클라이언트가 강제종료했을때를 알아보겠다.

과정은 간단하다.
클라이언트가 강제종료했을때는 read 반환값이 0 이 된다.  
반환값이 0 이란것을 알았으니 배열로 있는 클라이언트소켓(커뮤니티소켓)을 닫아줘야될것이다.
그리고 배열에서 클로즈하여 비어진 번지에 다른 소켓을 채워넣어줘야된다.   끝!!





위의 그림처럼..
 

클라이언트소켓배열0번지에 있는 4번 소켓이 강제 종료했을때는 
 

클라이언트소켓의 마지막 배열번지에 있는 소켓을 0번지에 넣는다.


 
9개의 클라이언트가 접속해 있을때 

마지막 접속한 9번 클라이언트가 강제종료했을때는 

MAX소켓 값을 -1 해주어야된다.



이것을 표현한 소스는 아래와 같다.

 server.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<bits/sockaddr.h>

#define MAXCLIEN  3
int main()  //(int argc, char *argv[])
{

  struct sockaddr_in stAddr;
  struct sockaddr_in stAccept;
  int iAddSize;
  int iVal;
  int iRet;
  int iCnum=0;
  int iCnt;
  
  int ban;
  
  int iAccept;  //  
  int iDs;  // 서버 소켓 식별자
  int iMaxDs;  // select 사용을 위해
  fd_set fsStatus;  // select 사용 소켓 식별자 구조체
  int iaClient[MAXCLIEN];
  
  unsigned char ucBuf[256];
  iAddSize= sizeof(struct sockaddr_in);
  iDs = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

  if0 > iDs)
  {
    perror("socket()");
    return -1;
  }
  bzero(&stAddr,sizeof(struct sockaddr_in));  // 구조체 초기화
  stAddr.sin_family = AF_INET;

  iVal =  inet_pton(PF_INET,"192.168.10.45",&stAddr.sin_addr.s_addr);
    
  printf("IP :%s\n", inet_ntoa(stAddr.sin_addr));  // 셋팅된 ip 값 확인
  
  stAddr.sin_port = htons(3000);  // port번호 
// 지역주소에 바인드  
  if(0 > bind(iDs, (struct sockaddr*)&stAddr, iAddSize) )
  {
    perror("bind");
    close(iDs);
    return -1;
  }
//----------------------------------------------------------------------
//여기까지는 서버와 클라이언트가 동일하다.
  
  if(0 > listen(iDs, 5))  // 소켓이 들어오는 요청을 처리할 수 있도록 설정
  {
    perror("listen");
    close(iDs);
    return  -1;
  }
  iMaxDs = iDs + 1;  //

  
  while(1)
  {
    printf("현재 접속자 수 :%d\n",iCnum);
    FD_ZERO(&fsStatus); // 초기화
    FD_SET(iDs,&fsStatus);  // 랑데뷰 소켓 감시
    FD_SET(0,&fsStatus);    // 0번까지 감시 (0번은 키보드)
    
    for(iCnt = 0; iCnt < iCnum; iCnt++)  // 열린 곳을 set 시키기
    {
      FD_SET(iaClient[iCnt],&fsStatus);
    }
    
    printf("select 통과전\n");
    ban = select(iMaxDs,&fsStatus,NULL,NULL,NULL);  // 입력이 있을때까지 대기
    printf("셀렉트 반환값 : %d\n",ban);
    if0 > ban)
    {
      perror("select");
      close(iDs);
      return -1;
    }  // 입력이 없으면 블럭이 된다.
    
    printf("select 통과후\n");
  
    if(1 == FD_ISSET(0,&fsStatus))  // 키보드가 왔는지 체크
    {
      iRet =  read(0,ucBuf,sizeof(ucBuf));
      printf("read iRet값 : %d\n",iRet);
      ucBuf[iRet] = 0;
      printf("%s\n",ucBuf);  
  
      for(iCnt=0 ; iCnum > iCnt ; ++iCnt)   
      {
        write(iaClient[iCnt],ucBuf,iRet); //서버에서 입력하면 접속한 클라이언트모두에게 뿌려줌
        
      }
      printf("첫번째 continue\n");
      continue;
    }
    
    if(1 == FD_ISSET(iDs,&fsStatus))  // 새손님이 왔는지 체크 
    {
      printf("커뮤니케이션에서 data가 들어왔다\n");
              
      // 실제 통신 시작
      iAccept = accept(iDs,(struct sockaddr*)&stAccept, &iAddSize);
      if(0 > iAccept )
      {
        perror("accept");
        close(iDs);
        for(iCnt = 0; iCnt < iCnum; iCnt++)
        {
          close( iaClient[iCnt] );
        }
        return -1;
      }
      if(MAXCLIEN <= iCnum)   // 사람이 많은지 체크
      {
        write(iAccept,"접속자가 많아 다음기회에...\n",sizeof("접속자가 많아 다음기회에...\n"));
        close(iAccept);
        continue;
      }
      if(iMaxDs == iAccept)   // 중간에 비면 빈 포트에 들어간다.
      {              // 그렇기 때문에 iMax가 늘어날 필요가 없다.
        iMaxDs=iAccept+1;
      }
      iaClient[iCnum]=iAccept;    // 소켓번호를  배열에 넣는다
      iCnum++;
    
      printf("Client IP :%s\n", inet_ntoa(stAccept.sin_addr));  // client ip 값 확인

      continue;
    }

    printf("커뮤니케이션 connect\n");
    for(iCnt=0 ; iCnum > iCnt ; ++iCnt)     
    {
      if(1 == FD_ISSET(iaClient[iCnt],&fsStatus))  // 사용자 수만큼 체크
      {
        iRet =  read(iaClient[iCnt],ucBuf,sizeof(ucBuf));         
        if(iRet == 0)   // 컨트롤+C 눌렀을때 반환값은 0 
        {
          close(iaClient[iCnt]);
          
          iaClient[iCnt] = iaClient[iCnum-1];

          if(iaClient[iCnt] == iMaxDs)
          {
            --iMaxDs;
          }
          --iCnum;
        }
        ucBuf[iRet] = 0;
        printf("%s\n",ucBuf);
    
        for(iCnt=0 ; iCnum > iCnt ; ++iCnt)   
        {
          write(iaClient[iCnt],ucBuf,iRet); //서버에서 입력하면 접속한 클라이언트모두에게 뿌려줌
        }

      }
    }
    
  }
  write(iAccept, "pong"sizeof("pong"));
  
  close(iAccept);
  close(iDs);
  
  return 0;
}

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

       iRet =  read(iaClient[iCnt],ucBuf,sizeof(ucBuf));         
        if(iRet == 0)   // 컨트롤+C 눌렀을때 반환값은 0 
        {
          close(iaClient[iCnt]);
          
          iaClient[iCnt] = iaClient[iCnum-1];

          if(iaClient[iCnt] == iMaxDs)
          {
            --iMaxDs;
          }
          --iCnum;
        }

컨트롤+C 를 눌렀을때 read의 반환값은 0 이된다. 
우선 강제종료된 클라이언트 소켓을 close 한다.
클라이언트소켓 마지막배열의 소켓 iaClient[iCnum-1] 값을 close하여 비게된 배열번지에 넣는다.
만약에 마지막번지에 있는 클라이언트소켓이 close했을때는 iMaxDs 값을 -1 해주게된다.

728x90