본문 바로가기

Project

Project05 멀티플렉스 기법을 이용한 멀티유저 채팅(작성중)

.멀티플렉싱란?
하나의 프로세스를 가지고 여러개의 클라이언트와 통신 할 수 있습니다 즉 전송로 1개로 많은 이들이 통신을 한다는 의미입니다 여러개의 클라이언트와 통신 할수 있는것이 장점이지만 선이 하나이므로 한쪽클라이언트와 통신중일 경우에는 다른 클라이언트는 기다려주어야합니다



참고>>인터럽트와 폴링방식이 결합된 방식



참고>>반이중 전이중?

.반이중 통신
양쪽 방향으로 신호의 전송이 가능하기는 하지만 경우에 따라 반드시 한쪽 방향으로만 전송이 이루어지게 한 방식을 말합니다.

주컴퓨터와 단말기가 반이중 방식으로 통신할 경우, 주컴퓨터가 단말기에 데이터를 보내는 동안은 단말기에서 데이터를 입력할 수 없으며, 반대로 단말기에서 데이터가 입력되고 있는 동안은 주컴퓨터가 단말기로 데이터를 보낼 수 없습니다

.전이중 통신
송신을 하면서 동시에 수신도 할 수 있는 방식을 말한다.


 
.구현기술
1)select함수를 이용하여 파일 디스크립터의 변화를 확인하고 블럭상태로 대기
2)저수준 입출력을 이용하여 데이터 전송 
3)시그널처리(ctrl + c)를 활용 


.select()사용


.server의 랑데뷰 소켓
accept를 하게되면 서버에 랑데뷰 소켓을 1차적으로 사용하게 됩니다 

랑데뷰 소켓이란 ?
 클라이언트에서 보내져서 큐상에서 대기하고 있는 정보(서버와 통신을 원하는 클라이언트의 정보)를 두번째 인자에 연결한 다음 클라이언트소켓과 연결된 새로운 소켓을 반환합니다
앞으로 그 클라이언트와의 통신은 반환된 소켓 디스크립터를 사용하게됩니다
반환값-- 성공시 communication 소켓 디스크립터, 실패시 -1을 반환
이 communicatoin 소켓을 이용하여 통신을 합니다

이러한 랑데뷰소켓은 4번부터 지정이되고 

 I err  랑데뷰   


서버로 접속하는 클라이언트는 5번부터 접속이됩니다

 I err  랑데뷰   클라이언트


.저수준 입출력 : 데이터 송수신


.시그널처리 (ctrl + c)
:통신중 종료를 처리하는 방법에는 여러가지가 있지만 2가지정도를 생가하였습니다 
1. 시그널즉 ctrl + c의 종료메세지를 눌렀을때 처리해주는 방법과
2. 종료 메세지를 입력받았을때 처리하는 방법입니다

2가지 방법으로 구현하는데 있어서 server과 사용하는 방법과 client를 사용하는 방법은 약간의 차이가 있습니다 client경우 열려 있는 소켓만 닫아주고 종료 메세지를 서버에 통지하면  되지만 서버의 경우에는 접속해있는 소켓을 모두 닫아주고 메세지를 통지해야되기 때문입니다
 


일단 시그널 처리를 이용하여 소켓을 닫습니다 시그널 관련 함수 처리는 아래와 같습니다 
Client

#include <signal.h>

/*전연변수 처리*/
int iDs;
unsigned char ucBuf[255];

/*시그널 선언*/
struct sigaction stFuncInfo;

memset(&stFuncInfo,0x00,sizeof(struct sigaction));
stFuncInfo.sa_handler = Socketclose;
sigaction(SIGINT,&stFuncInfo,NULL);


시그널이 발생 했을때의 처리하는 함수(SocketClose)를 만들어 줍니다 

void Socketclose(int iArg)
{
  printf("Client Close(ctrl+c)\n");
  write(iDs,"BYE!!\n",sizeof("BYE!!\n"));
  close(iDs);
  exit(0);
}


시그널이 발생하면 클라이언트에 메세지를 하나 출력하고 서버로 종료 메세지(BYE!!)를 보냅니다 그리고 소켓을 닫고 종료하게 됩니다

Server
client 많이 비슷 하지만 서버쪽에서는 함수에 각접소했는 클라이언트들을 모두 닫아주도록 합니다 

void sig_handler(int arg)
{
  printf("Server Close\n");
  for(iCnt = 0;iCnt < iCnum ;iCnt++)
  {
    write(iaClient[iCnt],"Server Close!(ctrl+c)",sizeof("Server Close!(ctrl+c)"));
    close(iaClient[iCnt]);
  }  
  close(iDs);
  exit(0);
}


결과보기>>
클라이언트에서 ctrl + c를 눌러 종료를 하니 종료 메세지가 뜨고 종료 하였습니다

다른 클라이언트는 종료되지 않고 대기중입니다

BYE!!란 메세지가 종료되면서 server로 전달되었습니다

>>여기까지 하면 약간의 문제점이 발생합니다 기타 문제점은 아래에서 모아 한꺼번에 다루었습니다

.문자종료 처리
: 문자종료처리란 특정 종료메세지를 입력했을때 종료하게 하는것입니다 이것은 서버에서 적으면 클라이언트와 서버가 종료되고 클라이언트에서 치면 서버에 종료메세지를 보내고 해당클라이언트만 종료되게 됩니다

"quit"를 받으면 종료하게 됩니다

Client - 들어온 메세지를 확인하여 종료합니다

    if(sizeof("quit")==iRet)
      {        
        if(0==memcmp("quit",ucBuf,iRet))
        {
          break;
        }
      }


Server - 각소켓에 메세지를 보내고 종료합니다

     if(sizeof("quit")==iRet)
       {
          if(0==memcmp("quit\n",ucBuf,iRet))
          {
            for(iCnt = 0;iCnum>iCnt;iCnt++)
            {      
              write(iaClient[iCnt],ucBuf,iRet);
            }
            break;
          }
        }


결과보기>>

quit을 누르면 종료됩니다

다른 클라이언트는 종료되지 않았습니다

서버역시 정상적으로 종료가 됩니다 


 
참고>>man sigaction
linux프로그램에 있어서 기본은 man페이지를 보는것과 구조체를 검색하여 보는 것입니다
맨페이지의 내용은 아래와 같습니다
함수포인터로 되어있으면 어떻게 사용해야되는지 잘 나와 있습니다

그리고 sigaction의 구조체는 /usr/include/bits/sigaction.h에 있습니다 grep을 이용하여 검색하면 쉽게 찾을수 있습니다

시그널과 문자처리를 하게되면 몇가지 문제점이 발생하게 됩니다 대응되는 처리는 아래와 같습니다

1문제점) 클라이언트 빈소켓 처리
:닫혀있는 소켓 빈 소켓상태인지확인 을 하기위하여 if문을 걸어주고 iRet == 0과 비교해줍니다 

if(1 == FD_ISSET(iDs,&fsStatus))
{      
  iRet = read(iDs,ucBuf,sizeof(ucBuf));
  if(0==iRet)
  {
    break;
  }


2문제점) 클라이언트에서 시그널을 발생하여 종료시 다른 클라이언트에게도 종료되는 현상 처리
:시그널이 발생하면(BYE!!)라는 메세지가 들어오게되는데 이메세지를 한번만 출력하고 다른 클라이언트에게는 보내지 않으면 됩니다

3문제점) 클라이언트가 서버로  종료메세지를 보내면 남은 클라이언트 모두 메세지가 전달되는 현상 처리
:"BYE!!"를 클라이언트에서 걸러주도록 하거나 서버에서 BYE!! 송신하지 않도록 처리하면됩니다