ACE_Reactor는 ACE_Event_Handler의 인스턴스를 관리합니다. ACE_Reactor::register_handler 함수로 관심있는 이벤트 타입을 설정합니다. ACE_Reactor::remove_handler 함수로 기존 등록된 이벤트 타입을 제거하도록 요청할 수 있습니다.
ACE_SOCK_Connector로 서버에 접속이 가능합니다.접속시 사용되는 ACE_SOCK_Stream 객체에 이벤트를 다음과 같이 등록가능합니다.
ACE_Time_Value vt(0,0);
connector_.connect(peer_,addr,&vt);
this->reactor()->register_handler(this,ACE_Event_Handler::CONNECT_MASK);
시간을 {0,0}으로 설정하고 호출하면 비동기 접속이 가능합니다. 또한 CONNECT_MASK로 이벤트를 등록하면 됩니다. 이렇게 등록하면 3개의 이벤트 핸들러가 호출됩니다.
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
virtual int handle_output (ACE_HANDLE fd = ACE_INVALID_HANDLE);
virtual int handle_exception (ACE_HANDLE fd = ACE_INVALID_HANDLE);
handle_input과 handle_exception는 서버 접속 실패시, handle_output는 서버 접속 성공시 호출됩니다.
서버 접속시 성공한 이벤트인 handle_output 이벤트 핸들러에서는 connector_.complete 함수를 호출해서 최종적으로 완료한 ACE_SOCK_STREAM에 대한 정보를 업데이트해야 ACE_Reactor에 이벤트를 등록할 수 있습니다.
서비스 객체는 스택에, 접속 객체는 힙에 동적으로 생성되고, 본연의 일이 마무리되면 힙에서 자동적으로 소멸되도록 설계했습니다.
서비스 객체를 힙에 생성하는 경우에는 CONNECTION MAP과 동기화 객체를 사용해서 제어해야 소멸된 객체를 참조하지 않도록 설계해야 합니다. CONNECTION MAP는 특정 아이디로 서비스 객체가 있는지를 확인할 수 있습니다. 문제는 서비스 객체를 사용하는 중에, 서비스 객체가 소멸하는 경우입니다. 이런 경우는 동기화 객체로 소멸 작업을 늦추도록 해야 합니다. 자세한 부분은 차후에 설명하도록 하겠습니다.
EchoService.h
#pragma once
#include <ace/Event_Handler.h>
#include <ace/Reactor.h>
#include <ace/SOCK_Stream.h>
#include <ace/Message_Queue.h>
#include <ace/Synch.h>
class EchoService:public ACE_Event_Handler
{
public:
enum CSTATE{
C_INIT,
C_CONNECTING,
C_FAIL,
C_SUCCESS
};
private:
ACE_SOCK_Stream peer_;
CSTATE state_;
ACE_Message_Queue<ACE_MT_SYNCH> send_datas_;
public:
EchoService(ACE_Reactor* reactor);
~EchoService(void);
void state(CSTATE state);
CSTATE state() const;
ACE_SOCK_Stream& peer();
virtual ACE_HANDLE get_handle (void) const;
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
virtual int handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask);
virtual int handle_output (ACE_HANDLE fd = ACE_INVALID_HANDLE);
int send(void* data, ssize_t len);
};
EchoService.cpp
#include "EchoService.h"
#include <iostream>
#include <ace/OS.h>
EchoService::EchoService(ACE_Reactor* reactor)
:ACE_Event_Handler(reactor),peer_(ACE_INVALID_HANDLE),state_(C_INIT)
{
}
EchoService::~EchoService(void)
{
}
void
EchoService::state(CSTATE state){
this->state_ = state;
if( C_SUCCESS!=this->state_) return;
this->reactor()->register_handler( this, ACE_Event_Handler::READ_MASK);
}
EchoService::CSTATE
EchoService::state() const{
return this->state_;
}
ACE_SOCK_Stream&
EchoService::peer(){
return this->peer_;
}
ACE_HANDLE
EchoService::get_handle (void) const{
return this->peer_.get_handle();
}
int
EchoService::handle_input (ACE_HANDLE fd/* = ACE_INVALID_HANDLE*/){
const int BUF =1024;
unsigned char in[BUF];
ACE_Time_Value rt(0,0);
ssize_t len = this->peer_.recv(in,BUF-1,&rt);
if( len<=0){// 서버 종료 또는 통신 에러
return -1;
}
in[len]=NULL;
std::cout<<std::endl<<"Server Data:"<<in<<std::endl;
return 0;
}
int
EchoService::handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask){
std::cout<<"Disconntected"<<std::endl;
ACE_Reactor_Mask m = ACE_Event_Handler::ALL_EVENTS_MASK | ACE_Event_Handler::DONT_CALL;
this->reactor()->remove_handler( this, m);
this->peer_.close();
this->peer_.set_handle(ACE_INVALID_HANDLE);
this->state(C_INIT);
this->send_datas_.flush();
return 0;
}
int
EchoService::send(void* data, ssize_t len){
if(C_SUCCESS!= this->state_) return -1;
ACE_Message_Block* mb = new ACE_Message_Block(len);
ACE_OS::memcpy( mb->wr_ptr(), data, len);
mb->wr_ptr(len);
this->send_datas_.enqueue_tail( mb );
this->reactor()->register_handler( this, ACE_Event_Handler::WRITE_MASK);
this->reactor()->register_handler( this, ACE_Event_Handler::