ACE Acceptor-Connector framework 2 – ACE_Svc_Handler 이해2


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;
}

- 목록:

18 thoughts on “ACE Proactor Framework 10 – Acceptor-Connector 예제 2

  1. 시리얼 통신강의를 빼고 모든 강의를 전부 시청하였습니다.
    이렇게 좋은 강의 자료를 만들어 주셔서 감사합니다.
    상용화 해도 손색이 없겠네요^^

  2. 훌륭한 강의에 감사드립니다.
    예제를 따라하다가 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로 나뉜것 같더라고요.

  3. 안녕하세요. 질문을 드리고자합니다.^^
    제가 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();
        이렇게 하니 아주 잘됩니다. ㅎㅎ
        헬프 답변 너무 감사드립니다.

        올려주신 강의를 하나 보았는데 정말 감명받았습니다.ㅜㅜ
        나머지 강의들도 보면서 모두 만들어 보려합니다.
        정말 존경스럽습니다.
        다시한번 감사드립니다. (–)(__)

  4. 이런 질문을 드려도 될지 모르겠는데…

    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가 나서 죽습니다.
        혹시 알고 계시면 알려주세요~~ 감사합니다. 강의 감사 드립니다.

  5. 안녕하십니까.
    이런질문도 드려도 될지…

    제가 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은 발생하지 않을 듯 합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>