ACE_Asynch_Acceptor, ACE_Asynch_Connector, ACE_Service_Handler 클래스를 활용하면 Proactor 기반 Acceptor-Connector 프로그램을 제작할 수 있습니다.
본 예제는 에코 클라이언트를 만드는 과정을 설명합니다.
EchoService.h
#pragma once
#include <ace/Asynch_IO.h>
#include <ace/Asynch_Connector.h>
#include <ace/Message_Block.h>
#include <ace/Message_Queue.h>
#include <ace/Synch.h>
class EchoService:public ACE_Service_Handler
{
private:
ACE_Asynch_Read_Stream read_require_;
ACE_Message_Block read_msg_;
ACE_Asynch_Write_Stream write_require_;
ACE_Message_Queue<ACE_MT_SYNCH> write_msg_queue_;
ACE_Atomic_Op<ACE_Thread_Mutex, bool> writable_;
public:
EchoService(void);
~EchoService(void);
virtual void open (ACE_HANDLE new_handle,ACE_Message_Block &message_block);
virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result);
virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result);
int send( const char* data);
private:
int recv_();
void close_();
};
template<class HANDLER>
class MY_CONNECTOR:public ACE_Asynch_Connector<HANDLER>
{
private:
HANDLER& obj_;
public:
MY_CONNECTOR(HANDLER& obj):obj_(obj)\
virtual HANDLER *make_handler (void){
return &obj_;
}
};
EchoService.cpp
#include "EchoService.h"
#include <iostream>
#include <ace/OS.h>
EchoService::EchoService(void)
:read_msg_(1024),writable_(false)
{
}
EchoService::~EchoService(void)
{
this->close_();
}
void
EchoService::close_(){
this->read_require_.cancel();
this->write_require_.cancel();
ACE_OS::closesocket( this->handle());
this->handle(ACE_INVALID_HANDLE);
this->write_msg_queue_.flush();
this->writable_ = false;
}
void
EchoService::open (ACE_HANDLE new_handle,ACE_Message_Block &message_block){
this->read_require_.open(*this,this->handle(), 0, this->proactor());
this->write_require_.open(*this, this->handle(), 0, this->proactor());
this->recv_();
this->write_msg_queue_.flush();
this->writable_ = true;
}
int
EchoService::send( const char* data){
if( ACE_INVALID_HANDLE==this->handle()) return -1;
size_t len = ACE_OS::strlen( data )+1;
ACE_Message_Block* m;
ACE_NEW_RETURN(m, ACE_Message_Block(len), -1);
m->copy( data );
if( this->writable_.value()){
this->writable_=false;
return this->write_require_.write(*m, m->length());
}
return this->write_msg_queue_.enqueue_tail(m);
}
int
EchoService::recv_(){
this->read_msg_.reset();
return this->read_require_.read(this->read_msg_, this->read_msg_.capacity()-1);
}
void
EchoService::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result){
if(! result.success() ){
result.message_block().release();
std::cout<<std::endl<<"Error Code:"<< result.error();
this->close_();
return;
}
if(0== result.message_block().length()){
result.message_block().release();
if( this->write_msg_queue_.is_empty()){
this->writable_= true;
return;
}
ACE_Message_Block* m;
this->write_msg_queue_.dequeue_head(m);
this->write_require_.write(*m, m->length());
return ;
}
this->write_require_.write( result.message_block(), result.message_block().length());
}
void
EchoService::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result){
if(! result.success() ){
std::cout<<std::endl<<"Error Code:"<< result.error();
this->close_();
return;
}
if(0== result.message_block().length()){
std::cout<<std::endl<<"Disconnted!!";
this->close_();
return;
}
result.message_block().copy("");
std::cout<<std::endl<<"RECV:"<<(const char*) result.message_block().rd_ptr();
this->recv_();
}
PEchoClient2.cpp
// PEchoClient2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <ace/ACE.h>
#include "EchoService.h"
#include <ace/Proactor.h>
#include <ace/Thread_Manager.h>
#include <string>
#include <iostream>
ACE_THR_FUNC_RETURN fn(void * arg){
EchoService* obj = (EchoService*) arg;
std::string cmd;
while(true){
std::cout<<std::endl<<"전송할 데이터를 입력하세요. 종료시 exit를 입력하세요."<<std::endl;
std::getline( std::cin, cmd );
if("exit"==cmd) break;
for( int i=0;i<1000;++i){
obj->send( cmd.c_str());
}
obj->send("end");
ACE_OS::sleep(6);
}
ACE_Proactor::instance()->proactor_end_event_loop();
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
ACE::init();
{
EchoService es;
MY_CONNECTOR<EchoService> cs(es);
cs.open(true,ACE_Proactor::instance(),true);
cs.connect(ACE_INET_Addr("192.168.0.2:1000"));
ACE_Thread_Manager tm;
tm.spawn(fn,&es);
ACE_Proactor::instance()->proactor_run_event_loop();
tm.wait();
}
ACE::fini();
return 0;
}
시리얼 통신강의를 빼고 모든 강의를 전부 시청하였습니다.
이렇게 좋은 강의 자료를 만들어 주셔서 감사합니다.
상용화 해도 손색이 없겠네요^^
정말 두손 모아 감사드립니다.
이 은혜 꼭 잊지 않겠습니다.
꾸뻑…..
훌륭한 강의에 감사드립니다.
예제를 따라하다가 ACE::init()과 ACE::fini() 를 헤더에서 찾을 수 없다는 에러를 내었습니다.
저는 ACE-6.3.1을 사용하였는데, ACE-6.1.9 버전으로 테스트 해 보니 또 잘 되더라구요.
6.3.1 에서는 include 아래에 를 추가해서 문제를 해결했습니다.
무슨 연유에서인지 저는 ACE.h 가 Init_ACE.h 를 포함하지 않았나봅니다.
혹시 같은 고민을 하시는 저 같은 초보자가 있을까 하여 로그를 남깁니다.
제가 아는 선에서는 예전 5 버전대에서는 ace/ace.h 에 초기화 로직이 있었던걸로 기억합니다.
그런데 6점대로 넘어가면서 ace/ace.h -> ace/ACE.h 등으로 세분화? 되면서
ACE::init(), ACE::fini()가 ace/Init_ACE.h로 나뉜것 같더라고요.
정말 감사합니다..
강의 하나하나가 정말 아름답네요..
안녕하세요. 질문을 드리고자합니다.^^
제가 ace를 이용한 네트워크 프로그램하나를 인수받았는데요.
클라이언트를 62개 이상 받아들이면 오류가 나는겁니다. 그래서 찾아보니
ACE_Reator 를 사용하여 개발된 중계서버 문제. 수백 또는 수천개의 핸들을 다중 수신하도록 설정이 가능한 ACE_Select_Reator, ACE_TP_Reator와는 달리, ACE_WFMO_Reator는 62개 이상의 핸들은 처리할 수 없다. 이 제한은 Windows에서는 단지 WaitForMultipleObject()함수의 스레드당 대기 가능 핸들 개수가 64개라는 사실때문이다.
ACE_WFMO_Reator는 내부적으로 64개의 핸들중 2개를 별도로 사용하기 때문에 62개의 핸들만 사용가능하고, Windows에서 ACE_Reator를 사용시에는 디폴트로 ACE_WFMO_Reator가 Base 이다.
-이 증상을 해결하려면 ACE_Proactor를 사용하거나, ACE_Select_Reator를 사용해야한다.
이렇게 나와있더라구요. 저는 ace를 이용해본적이 없어서 급한마음에 책을 보고 ACE_Select_Reator로 대처를 하려고 하는데요.
m_pAcceptor = new ClientAcceptor;
m_pAcceptor->reactor( ACE_Reactor::instance() );
이부분을
ACE_Select_Reactor sr;
m_reactor = new ACE_Reactor(&sr);// ACE_Reactor* m_reactor;
m_pAcceptor->reactor( ACE_Reactor::instance(m_reactor) );
이렇게 수정하니 62개이상의 클라이언트 오류는 사라졌지만..
m_reactor->reset_reactor_event_loop();
m_reactor->run_reactor_event_loop();
이부분에서 죽더라구요 ㅜㅜ
ace 강의를 심도있게 들은 후 공부좀 해서 하고싶지만 시간이 없는 관계상 이렇게 질문드려봅니다.
어떤 다른 부분을 수정해야 하는지 궁금합니다.
이렇게 질문부터 드려서 죄송합니다.
일단 기존 64개 이상 핸들링 문제는 잘 해결하신 듯하군요.
전체 코딩을 보지 않아서 정확하게 답변드리긴 힘들어보이는군요.
일단 reset_reactor_event_loop를 호출하는 이유는 파악하셔야 할듯 합니다. 일반적으로 run_reactor_event_loop만 호출만 되는데 .. 말이죠.
일단 reset_reactor_event_loop를 호출하면 sr이 비활성화 상태로 들어갑니다. 비활성화 상태가 되면, 자신이 관리하던 핸들러에 대해 소거 작업이 이후로 진행될 터인데, 이 과정에서 오류가 발생하는 듯하군요.
결국 reset_reactor_event_loop를 호출해야하는 이유를 파악하셔야 할듯합니다. 어지간해서는 호출할 일이 없을 듯한데.
아.. 답변 감사합니다~ 꾸벅~
알고보니 제가 기초적인 실수를 했습니다. ㅎㅎ
m_reactor->reset_reactor_event_loop();
m_reactor->run_reactor_event_loop();
이부분이 svc() 스레드에서 돌아가는것인데..
ACE_Select_Reactor sr;
m_reactor = new ACE_Reactor(&sr);// ACE_Reactor* m_reactor;
이부분은 스레드 돌아가기전 제가 만든 init()함수에서 작성한 코드라..
레퍼런스로 넘긴 sr의 핸들이 소멸되버려서 발생한 런타임 purecall()오류인것이였습니다.
그래서..
m_tp = new ACE_TP_Reactor;
m_reactor = new ACE_Reactor(m_tp);
m_pAcceptor->reactor(m_reactor);
멤버변수로 생성하여..
m_reactor->reset_reactor_event_loop();
m_reactor->run_reactor_event_loop();
이렇게 하니 아주 잘됩니다. ㅎㅎ
헬프 답변 너무 감사드립니다.
올려주신 강의를 하나 보았는데 정말 감명받았습니다.ㅜㅜ
나머지 강의들도 보면서 모두 만들어 보려합니다.
정말 존경스럽습니다.
다시한번 감사드립니다. (–)(__)
이런 질문을 드려도 될지 모르겠는데…
Service Configurator Framework 2 – ACE_Service_Config 클래스 이해 에서
remove Commander 로 자기자신을 죽일 때
if( 0 == in[len-1]){
std::cout<<std::endl<<"User Command:"<<msg_.rd_ptr()<< std::endl;
ACE_Service_Config::process_directive(msg_.rd_ptr());
std::cout<<"after process_directive"<<msg_.rd_ptr()<<std::endl;
msg_.length(0);
msg_.crunch();
}
process_directive 를 거치면서, 프로그램이 죽네요.. 디버깅을 타보니, msg_.rd_ptr() 이 directive[] 로 연결이 되는데, 이 포인터가 의미가 없는 값이 들어있네요.. Bad_ptr 로…
제 생각으로는, Commander 가 죽으면서, Bad_ptr 이 되어버린거 같은데…
어떻게 해결할 수 있을지, 감이 잘 안옵니다… ㅠㅠ;
fini() 까지는 호출이 되고 나서 죽습니다…
ServiceLoader 에서 스트링을 참조하려다가 죽는듯한데…
(글 수정이 안되는게 좀 불편하네요~ㅎㅎ, 강의, 감사하게 보고 있습니다.)
svc.conf 파일이 정상적인지 일단 확인하셔야 할 듯합니다.
단순 문자열 관련 에러라면, 조금만 노력하시면 에러를 잡아낼 수 있을 겁니다.
조금 더 자세한 정보를 제공해준다면, 조금 더 도움이 되는 답변이 가능합니다.
저도 같은 문제거 같습니다. fini()까지 호출되고 main()에서 error가 나서 죽습니다.
혹시 알고 계시면 알려주세요~~ 감사합니다. 강의 감사 드립니다.
감사합니다.
제가 운영중인 페이스북 카페에 가입해, 조금 더 많은 C++에 대한 정보를 얻으세요.
https://www.facebook.com/groups/OpenCPP/
안녕하십니까.
이런질문도 드려도 될지…
제가 window mfc dialog 기반에서 Proactor을 이용해 서버를 만들려고 하는데
ACE_Asynch_Acceptor의 open 함수를 호출하면 정상적으로 동작은 되는데, 종료시(런타임시..)
memory leak이 발생합니다.
mfc 가 아닌 win32 console 로 프로젝트를 생성하면 memory leak이 발생하지 않습니다.
혹시 이 문제에대한 해결방법이 있을까요? 도움이 필요합니다?
감사합니다.
해당 momory leak 현상은 proactor thread 종료된 후
accept close가 호출됨으로 cancel io 대한 처리 부분이 에러가 발생한 경우일듯 합니다.
(close도 요청되는 요청 작업이고, 해당하는 응답 처리가 되어야 하지만, 이미 쓰레드가 죽었음으로 이런 경우엔 불가능합니다.)
따라서 accept close 후 proactor thread 종료로 변경시 해당 leak은 발생하지 않을 듯 합니다.
정말 감사합니다.
강의 잘 들었습니다.
Highly descriptive post, I loved that bit. Will there be a part 2?
Hello. And Bye.
윈도우OS 에서 사용할 ACE 공유 메모리 class 가 있을까요? ACE_Shared_Memory_SV 는 open 에러가 나네요..
ACE_Shared_Memory_MM를 사용하셔야 할듯 합니다. _SV는 system V 환경에서만 지원합니다.
http://www.dre.vanderbilt.edu/Doxygen/5.7.1/html/ace/a00494.html
참고 문헌을 보고 들으니 더욱 이해가 잘되네요.
감사합니다.