[출처 : http://kldp.org/node/63623]

코딩은 해당업체에 룰을 따랐습니다. 헤더만을 올려놓습니다. 소스는 쫌...엉망입니다...앞서 이야기 했던 네트워크처리를 위한 부분입니다.

#ifdef __cplusplus
extern "C" {
#endif

/******************************************************************************
 DISPATCHER			: Dispatcher 식별자
 size_t size		: 현재 등록되어 있는 fd 수
 size_t maxfds		: 최대 등록 가능한 fd 수(필요에 따라 수시로 증가)
 pollFDs			: 현재 사용하고 있는 struct pollfd 구조체 포인터
******************************************************************************/
typedef struct
{
	size_t size;
	size_t maxfds;
	struct pollfd *pollFDs;
} DISPATCHER;

/******************************************************************************
 Dispatcher_Create(const size_t maxfds)
 Dispatcher_Create는 calloc를 통해 DISATCHER구조체를 생성하고 생성된 구조체를
 반환한다.
******************************************************************************/
DISPATCHER *Dispatcher_Create();


/******************************************************************************
 Dispatcher_Destroy(DISPATCHER *dispatch)
 Dispatcher_Create를 통해 생성된 구조체를 free를 통해 소멸시킨다.
******************************************************************************/
void Dispatcher_Destroy(DISPATCHER *dispatch);


/******************************************************************************
 Dispatcher_Add(DISPATCHER *dispatch)
 DISPATCHER의 식별자에 fd를 추가함으로써 poll이 fd를 감시할 수 있도록 한다.
******************************************************************************/
int Dispatcher_Add(DISPATCHER *dispatch, const int fd);


/******************************************************************************
 Dispatcher_Del(DISPATCHER *dispatch)
 DISPATCHER의 식별자에 fd를 삭제함으로써 poll이 fd를 감시에서 제거한다.
******************************************************************************/
int Dispatcher_Del(DISPATCHER *dispatch, const int fd);


/******************************************************************************
 Dispatcher_Wait(DISPATCHER *dispatch, const long timeout)
 등록된 fd들을 감시한다. 또 한 이 함수를 사용한 시점에서는 Block되며 이벤트
 가 발생할때까지 대기하게 된다. timeout으로 대기시간을 설정하게 되면 대기시
 간 이후에 함수를 빠져나가게 된다. 대기시간은 밀리세컨드이다.
******************************************************************************/
int Dispatcher_Wait(DISPATCHER *dispatch, const long timeout);


/******************************************************************************
 Dispatcher_CurfdsSize(DISPATCHER *dispatch) 
 DISPATCHER 에 현재까지 추가된 소켓의 수를 반환한다.
******************************************************************************/
size_t Dispatcher_CurfdsSize(DISPATCHER *dispatch);

#ifdef __cplusplus
}

poll을 객체형식으로 만들어 놓은겁니다. poll의 지저분한(?) 고유 코드는 다 빠지고 이것만으로 하니 별 신경쓸께 없었습니다. 위의 함수군을 사용하는 일은 다른 사람은 없었습니다. 이것은 밑의 Reactor을 만들기 위해 만들어 놓은 것이죠. 써도 별상관 없겠지만 이걸 익히려 노력은 안하리라 생각했기 때문입니다.

다음은 Reactor패턴을 C수준에서 응용한겁니다. 더 세밀히 하자면 함수포인터를 사용해야 했으나..그럴만한 시간적..ㅜㅜ..

Client Handler(CHandler)는 접속된 각각의 개별 커넥션을 말합니다. 그리고 이것들의 이벤트를 관리하며 처리하는 Reactor이 존재합니다. 사용자는 CHandler의 이벤트 발생시에 처리할 부분을 작성하게 됩니다. 즉 비지니스 로직이 들어갑니다.

Reactor은 TCPCLIENT 객체를 추가하게 되면 CHANDLER(Client Handler)를 생성하게 됩니다. 그리고 CHandler_*함수를 각 이벤트 분류에 따라 호출하게 해줍니다. '유저가 작성해야할 함수' 가 가상함수부분인데...여하튼 이 부분을 사용자가 작성해야 전체가 완성됩니다. 이 부분은 비지니스 로직이 들어가야할 부분이죠. 이것을 바탕으로 OnPacket(..)함수등을 만들어 패킷 처리를 하고 그랬었습니다.

CHandler 클래스를 상속받고, 가상함수등을 이용하고 Reactor에 등록시에도 그렇게 하여야 겠으나 그냥 어찌어찌 만들어진 코드입니다. 핑계를 데면 급히 만든 코드이니......... 또 관리자가 STL을 모른다른 이유로 또 C라는 한정적인 이유로 그냥 배열로 CHandler들을 관리하게 될 허접 SOCKETBOX를 만들었습니다.

그리 잘 따른 코드는 아니나 프레임웍이라 생각하고 보신다면 비지니스로직과 네트워크단의 분류가 확실히 되었음을 아실겁니다.

비지니스로직단에서는 마치 쓰레드를 사용하는것 마냥 착각하게 됩니다.

#ifdef __cplusplus
extern "C" {
#endif

typedef struct
{
	SOCKETBOX	*socketBox;
	DISPATCHER	*dispatcher;
	void		*mainArg;
} REACTOR;

typedef struct
{
	REACTOR		*reactor;
	TCPCLIENT	*tcpClient;
	TIMESPEC	 timeout;
	TIMESPEC	 lastAccessTime;
} CHANDLER;


//유저가 작성해야 하는 함수==================================================
//연결되었을때 호출
int  CHandler_Open(CHANDLER *cHandler, TCPCLIENT *tcpClient, void *mainArg);
//데이터가 들어왔을때 호출
int  CHandler_Input(CHANDLER *cHandler, TCPCLIENT *tcpClient);
//연결이 끊겼을때 호출
void CHandler_Close(CHANDLER *cHandler, TCPCLIENT *tcpClient);
//예약한 시간설정이 되었을때 호출
int  CHandler_TimeOut(CHANDLER *cHandler, TCPCLIENT *tcpClient);

//  REACTOR 생성시 호출될 함수
void  Reactor_Open(REACTOR *Reactor);
//  REACTOR 타임아웃시 호출될 함수
void  Reactor_TimeOut(REACTOR *Reactor);
//  REACTOR 파괴시 또는 종료시 호출될 함수
void  Reactor_Close(REACTOR *Reactor);
//===========================================================================


//  REACTOR 생성자
REACTOR  *Reactor_Create(REACTOR *mainArg);
//  REACTOR 파괴자
void  Reactor_Destroy(REACTOR *Reactor);
//  REACTOR 실행
void  Reactor_Run(REACTOR *Reactor);

//유저가 사용할 수 있는 함수=================================================
//새로운 tcpClient를 등록
int   Reactor_Add(REACTOR *Reactor, TCPCLIENT *tcpClient);
//tcpClient를 삭제
int   Reactor_Del(REACTOR *Reactor, TCPCLIENT *tcpClient);

//타임아웃 설정
void  CHandler_SetTimeOut(CHANDLER *cHandler const int sec);

//fd값을 돌려줌
int   CHandler_Getfd(CHANDLER *cHandler);

//최근 접근 시간 셋팅함.
int   CHandler_SetAccessTime(CHANDLER *cHandler);

//최근 접근 시간 셋팅후 지난 시간을 밀리세컨드로 반환
long  CHandler_GetPastTime(CHANDLER *cHandler);

//자신의 아규먼트를 저장
int   CHandler_SetUserArg(CHANDLER *cHandler void *userArg);

//해당 핸들러에 저장한 아규먼트를 가져옮
void *CHandler_GetUserArg(CHANDLER *cHandler);

//생성시 저장했던 아규먼트를 가져옮
int   CHandler_GetMainArg(CHandler *cHandler);

//TCPCLIENT객체를 가져옮
TCPCLIENT *CHandler_GetTcpClient(CHandler *cHandler);

#ifdef __cplusplus
}
#endif

편집과정에서 몇가지 빠진함수가 있기도 하네요.

위와같이 작성하고 CHandler에서 사용할 변수도 CHandler_SetUserArg로 구조체를 통해 등록하여 사용했습니다. 따라서 전역변수를 일체 사용하지 않아도 되는 코드를 생성할 수 있었으며 연결당 개별관리를 Reactor가 CHandler이란것을 통해 해주었으며 CHandler_Open등에서 -1을 리턴하면 무조건 CHandler_Close를 호출하도록 하여 Close함수를 쓸 이유도 없게 되었구요.

특히나 편리했던 부분은 TimeOut부분을 Reactor에서 관리하고 이벤트처리하여 주므로 프로토콜 문서에 따라 복잡해질 로직이 아주 심하게 편해졌다고 말씀 드릴 수 있네요..TimeOut은 커넥트당 개별관리를 Reactor에서 해줍니다. 일반적으로 처리하는 방식인 일정 시간마다 전체 루프를 돌며 하는것이 아니기 때문에 좀 더 안정적이라고 할 수 있겠죠.

이것을 사용한 결과...다른사람이 사용함으로써 생기는 문제에 있어서 제가 그 사람의 비지니스 로직까지 알 필요 없이 이 부분에 대해서만 점검하거나 버그 수정정도에서 마무리 됩니다. 전 이에 관련된 문서나 알고리즘(?) 정도만 철저히 하면 제가 맡은 소켓 라이브러리 부분은 완료 되는 것이었죠.

다른 사람들은 커넥트되는것을 또 데이터가 들어오는것을, 타임아웃되는것을 직접 코드로 작성할 이유가 없었습니다. 그냥 이벤트가 들어오는 데로 처리하면 되는거니까요. 최소한의 지식은 커넥트와 센드, 서버로 띄우는것인데요. 이것들도 랩핑하여 라이브러리화 했습니다.

아마도 C만 하신 분들은 아주 심하게 생소하리라 추측됩니다. 이의가 있으신분은 언제든지요.....

실력자분들이 엄청 많을텐데...허접한 코드....마니 챙피하네요... :(



[출처 : http://kldp.org/node/63623]

+ Recent posts