ACE_Connector 클래스를 사용할 때는 서비스 객체 소유권 문제에 대한 고민을 해야 합니다.
ACE_Svc_Handler 클래스로부터 만들어진 서비스 객체는 힙에 생성된 서비스 객체는 자동적으로 제거됩니다. 따라서 스택에 서비스 객체를 생성하면 서비스 객체에 대한 소유권을 프로그래머가 가질 수 있습니다. 조금 더 섬세한 제어를 원한다면 ACE_Svc_Handler 클래스를 상속받아 원하는 형태로 디자인해야 합니다.
서비스 객체의 상태 정보를 관리할 필요가 있습니다. 접속 성공 여부, 서비스 끊김 여부, 타임아웃 여부와 같은 상태를 관리할 필요가 있습니다.
마지막으로 접속 방식에 대한 고민입니다. ACE_Synch_Options를 사용해서 비동기 방식 접속이나 동기 방식 접속을 사용할 수 있으며, 접속 시간에 대한 Timeout를 역시 설정할 수 있습니다.
EchoService.h
#pragma once
#include <ace/Svc_Handler.h>
#include <ace/Synch.h>
#include <ace/SOCK_Stream.h>
#include <ace/Event_Handler.h>
class EchoService:public ACE_Svc_Handler<ACE_SOCK_Stream, ACE_NULL_SYNCH>
{
public:
enum S_STATE{
S_INIT,
S_CONNNECT_SUCCESS,
S_CONNNECT_FAIL,
S_DISCONNECT,
S_TIMEOUT,
};
private:
S_STATE state_;
typedef ACE_Svc_Handler<ACE_SOCK_Stream, ACE_NULL_SYNCH> PARENT;
public:
ssize_t send(const char* data);
public:
EchoService(void);
~EchoService(void);
virtual int open (void * = 0);
virtual int close (u_long flags = 0);
virtual int handle_timeout (const ACE_Time_Value &time,const void *);
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
};
EchoService.cpp
#include "EchoService.h"
#include <iostream>
EchoService::EchoService(void)
:state_(S_INIT)
{
}
EchoService::~EchoService(void)
{
}
int
EchoService::open (void * data){
state_ = S_CONNNECT_SUCCESS;
std::cout<<std::endl<<"S_CONNNECT_SUCCESS";
return PARENT::open(data);
}
int
EchoService::close (u_long flags){
state_ = S_CONNNECT_FAIL;
std::cout<<std::endl<<"S_CONNNECT_FAIL";
return PARENT::close(flags);
}
int
EchoService::handle_timeout (const ACE_Time_Value &time,const void * arg){
state_ = S_TIMEOUT;
std::cout<<std::endl<<"S_TIMEOUT";
return PARENT::handle_timeout(time, arg);
}
int
EchoService::handle_input (ACE_HANDLE fd/* = ACE_INVALID_HANDLE*/){
char buf[1024];
ssize_t len = this->peer().recv( buf, 1024);
if(len<=0){
state_ = S_DISCONNECT;
std::cout<<std::endl<<"S_DISCONNECT";
return -1;
}
buf[len]=0;
std::cout<<std::endl<<"Recv Data:"<<buf<<std::endl;
return 0;
}
ssize_t
EchoService::send(const char* data){
if( S_CONNNECT_SUCCESS!=this->state_) return -1;
return this->peer().send(data, ACE_OS::strlen( data )+1);
}
EchoClient2.cpp
// EchoClient2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <ace/ACE.h>
#include "EchoService.h"
#include <ace/Connector.h>
#include <ace/SOCK_Connector.h>
#include <iostream>
#include <string>
#include <ace/Thread_Manager.h>
#include <ace/OS.h>
ACE_THR_FUNC_RETURN fn(void *arg){
EchoService* client= (EchoService*)arg;
std::string cmd;
while(true){
std::cout<<std::endl<<"전송할 데이터를 입력하세요.종료시 exit를 입력하세요."<<std::endl;
std::getline( std::cin, cmd);
if( "exit"==cmd){
ACE_Reactor::instance()->end_reactor_event_loop();
break;
}
ssize_t len = client->send( cmd.c_str() );
if(len<=0){
std::cout<<std::endl<<"서버로 전송 실패입니다.";
}
ACE_OS::sleep(1);
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
ACE::init();
{
typedef ACE_Connector<EchoService,ACE_SOCK_Connector> ECHO_CLIENT;
ECHO_CLIENT con(ACE_Reactor::instance(), ACE_NONBLOCK);
ACE_SOCK_Connector::PEER_ADDR addr("192.168.0.2:1000");
EchoService client;
EchoService* p = &client;
ACE_Synch_Options op;
op.set( ACE_Synch_Options::USE_REACTOR,ACE_Time_Value(0,400) );// 비동기방식+시간 제한
//op.set( 0,ACE_Time_Value(0,400) );// 동기방식+시간 제한
con.connect(p,addr,op);
ACE_Thread_Manager tm;
tm.spawn(fn,&client);
ACE_Reactor::instance()->run_reactor_event_loop();
tm.wait();
}
ACE::fini();
return 0;
}