프로젝트/libSOPT2010.03.24 17:16


libsopt-docs - 프로젝트 메뉴얼 및 API 문서

libsopt-src.vol1, libsopt-src.vol2 - 프로젝트 소스파일


ACKNOWLEDGEMENTS

libsopt have been deeply influenced and improved by the following members of SOPT development part.

Yeon-Bok, Lee <SOPT 2th. development part manager>
Sung-Min, Ahn <SOPT 3th. development part manager>
Min-A, Park   <SOPT 2th. development part member>
So-Jung, Kim  <SOPT 3th. development part member>
Eun-Hyang, Ga <SOPT 3th. development part member>

we would also like to thank all the following people who have also contributed to libsopt

WoC: http://woc.openmaru.com/blog1


This is libsopt version 1.0.0, released Fri Mar 16 17:23:23 GMT+09:00 2009

If you have any problems with or questions about libsopt,
please send email to the SOPT development manager(develop@sopt.org).

Copyright 2008-2009 SOPT(Shout Our Passion Together)
contact us: http://sopt.org

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 17:01
libSOPT를 이용하여 비동기 echo server를 작성한 예입니다. 

1. Echo_Request_Handler 작성하기.
가장먼저 제공할 서비스(본 예에서는 echo 서비스)에 대한 통신 프로토콜을 정의합니다.

TCP를 통한 네트웍에서는 데이터가 byte stream형태로 송수신 되는데 이것을 적당히 분리해 처리 가능한 객체로 재구성하는 과정이 필요합니다. libSOPT에서는 이러한 처리를 위해 모든 프로토콜을 Header, Payload로 구분하게 됩니다.

  • Header : urgent_request, header_length, payload_length를 포함한다.
    urgent_request - 해당 Request가 Executor로 전달되어 처리되어야 하는지, Executor에 전달되지 않고 바로 처리되어야 하는지를 나타낸다.
    header_length - Header의 길이.
    payload_length - Header뒤에 따라오는 payload의 데이터 길이
  • Payload : Header의 뒤에 따라오는 application-specific한 데이터.

본 예제에서는 Header의 길이는 4바이트 고정길이를 가지며, 처음 2바이트가 urgent_request를 마지막 2바이트가 payload_length를 나타냅니다.

프로토콜의 정의를 마치고 Request_Handler::call() 함수를 오버라이드 합니다.
call() 함수는 해당 서버에서 클라이언트로 제공해야할 서비스를 담고 있는 가장 중요한 부분입니다. 즉, 서버에서 어떤 서비스를 제공하느냐에 따라 해당 함수의 구현이 달라지게 되는 것입니다.

다음은 구현이 완료된 Echo_Request_Handler입니다.
// -*- C++ -*-

//=============================================================================
/**
*  @file    echo_request_handler.h
*
*  $Id: echo_request_handler.h Fri Mon 16 17:23:23 GMT+09:00 2009
*
*  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
*/
//=============================================================================

#ifndef _ECHO_REQUEST_DEFINE
#define _ECHO_REQUEST_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "engine/request_handler.h"

using namespace libsopt;

/**
  * @class Echo_Request_Handler
  * 
  * @brief Request_Handler상속하는 Echo_Request_Handler 예제 클래스.
  * 
  * 다음 4개의 메소드를 재정의 합니다.
  *		- header_length(): Header의 크기를 반환합니다.
  *		- payload_length(): Payload의 크기를 반환합니다.
  *		- urgent_request(): urgent를 구분합니다.
  *		- call(): Echo 서비스 루틴입니다.
  *
  * Header는 다음과 같이 4바이트로 구성됩니다.
  *		- 최초 2바이트는 urgent 구분 바이트입니다.
  *		- 다음 2바이트는 Payload의 길이를 나타냅니다. Payload의 길이는 Echo 메세지의 길이입니다.
  *
  * Payload는 Echo 메세지 입니다.
  *		- 가변길이 입니다.
  */
class Echo_Request_Handler :
	public Request_Handler {
public :

	enum { HEADE_LENGTH = 4, URGENT_OFFSET = 0, PAYLOAD_OFFSET = 2 };

  /**
	* @retval 4 Header의 길이
	*/
	size_t header_length()
	{ return HEADE_LENGTH; }

  /**
	* @retval size_t Payload의 길이
	*/
	size_t payload_length()
	{ return read_data<u_short>(PAYLOAD_OFFSET); }

  /**
	* @retval 0 urgent 무시
	* @retval >0 urgent 처리
	*/
	int urgent_request()
	{ return read_data<u_short>(URGENT_OFFSET); }

  /**
    * Echo 서비스 루틴
	* 
	* @retval 0 정상처리
	* @retval -1 Echo 처리 오류
	*/
	int call()
	{
		if( this->stream_handler()->has_valid_handle() == 1 )
		{
			this->stream_handler()->asynch_write_n(this->duplicate(), this->length());
			return 0;
		}
		return -1;
	}
};

#endif

2. Echo_Server 작성하기.
libSOPT::Server의 입장에서는 Request_Handler를 상속하는 어떤 Request객체를 생성해야하는지 알수 있는 방법이 없습니다.(본 예제에서는 Echo_Request_Handler) 

따라서 Server::make_request_handler()와 Server::release_request_handler()함수는 Echo_Server에서 반드시 오버라이드 해야합니다. 순수가상 함수로 선언되어 있기 때문에 오버라이드 하지 않을경우 컴파일러 에러가 발생하게 됩니다.

또한 본 예제에서는 Executor를 생성하는 make_executor()함수를 오버라이드 하게됩니다. 
Executor는 Request객체를 Dispatch하는 쓰레드풀로서 Singgle_Executor, Fixed_Executor, LF_Executor를 기본으로 제공합니다. 추가적인 Executor를 필요로한다면 libSOPT::Executor의 인터페이스를 상속하여 구현할수 있습니다.

Server::make_executor()함수의 기본구현은 Single_Executor를 생성합니다.

다음은 구현이 완료된 Echo_Server입니다.
// -*- C++ -*-
 
//=============================================================================
/**
*  @file    echo_server.h
*
*  $Id: echo_server.h Fri Mon 16 17:23:23 GMT+09:00 2009
*
*  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
*/
//=============================================================================

#ifndef _ECHO_SERVER_DEFINE
#define _ECHO_SERVER_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "engine/server.h"
#include "engine/executor.h"
#include "engine/fixed_executor.h"
#include "echo_request_handler.h"

using namespace libsopt;

/**
  * @class Echo_Server
  *  
  * @brief Server상속하는 Echo_Server 예제 클래스.
  *
  * 다음 3개의 메소드를 재정의 합니다.
  *		- make_executor(): Fixed_Executor 객체 포인터를 반환합니다.
  *		- make_request_handler(): Echo_Request_Handler 객체 포인터를 반환합니다.
  *		- release_request_handler(): Echo_Request_Handler 객체를 해제합니다.
  */
class Echo_Server : 
	public Server {
protected :

  /**
	* Executor 구현체를 생성해 반환합니다.
	*
	* @retval Executor * Fixed_Executor 객체 포인터
	*/
	virtual Executor * make_executor()
	{
		Executor *e = new Fixed_Executor;
		e->server(this);
		return e;
	}

public :

	Echo_Server(LogMgr *logger, Config *config)
		: Server(logger, config)
	{ /* empty */ }

  /**
	* Echo_Request_Handler 객체를 생성해 반환합니다.
	*
	* @retval Request_Handler * Echo_Request_Handler 객체 포인터
	*/
	virtual Request_Handler * make_request_handler()
	{ 
		Request_Handler *r = new Echo_Request_Handler;
		r->server(this);
		r->size(config_->RECV_BUFFER_SIZE);
		return r;
	}

  /**
	* Echo_Request_Handler 객체를 해제합니다.
	*
	* @param Request_Handler * Echo_Request_Handler 객체 포인터
	*/
	virtual void release_request_handler(Request_Handler *r)
	{ r->release(); }
};

/**
  * @class Echo_Server_Controller
  *  
  * @brief Server::Controller상속하는 Echo_Server_Controller 예제 클래스.
  *
  * 기본 Controller의 서비스를 이용합니다.
  */
class Echo_Server_Controller :
	public Server::Controller {
protected :

	/// Server::Controller::svc()를 호출합니다.
	virtual int svc()
	{ return Server::Controller::svc(); }

public :

	explicit Echo_Server_Controller(Server *serv)
		: Server::Controller(serv)
	{ /* empty */ }
};

#endif
마지막으로 main함수의 구현은 다음과 같습니다.
#include "engine/config.h"
#include "engine/logmgr.h"
#include "engine/logmgr_task_start_hook.h"
#include "echo_server.h"

using namespace libsopt;

int ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{
	ACE_UNUSED_ARG(argc);
	ACE_UNUSED_ARG(argv);
	
	LogMgr *l = new LogMgr;
	Config *c = new Config;
	l->put_logger(ACE_LOG_MSG);
	if( c->read_config("echo_server.ini") == -1 )
		return -1;

	Server *serv = new Echo_Server(l, c);

	LogMgr_Task_Start_Hook start_hook(l);
	start_hook.make_hook();
	
	if( serv->run_server_process() != -1 )
	{
		Echo_Server_Controller controller(serv);
		controller.run_server_controller_process();
		controller.wait();
		serv->end_server_process();
	}
	ACE_DEBUG((LM_INFO, "*** Bye~\n"));
	return 0;
}
libSOPT를 통해 많은 양의 코드가 재활용되고 있음을 확인할 수 있습니다. 
또한 서비스 개발자는 클라이언트 서버간의 통신과 같은 세세한 부분을 떠나, 서비스를 제공하기 위한 비지니스 로직에 보다 신경쓸수 있음을 위 예제를 통해 알아보았습니다.

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 16:20
1. 개요
클라이언트로 서비스를 제공하는 Server 인터페이스 클래스.
클라이언트로부터 요청을 처리하는 일반적인 서비스를 정의하는 인터페이스 클래스입니다. Acceptor와 Executor의 구현제를 소유하며, 서비스 처리에 대한 각종 통계정보를 수집합니다.

Server 클래스는 최소한의 작업을 통해 application-specific하게 재정의 될수 있어야 합니다. application-specific하도록 서버를 재구성하기 위해서는 Request_Handler를 상속하는 새로운 Request_Handler를 정의해야하며, 추상 클래스인 Server에서는 미래에 특정 목적을 가지고 작성될 새로운 Request_Handler를 알수 없기때문에, Server를 상속하는 새로운 서비스 객체에서 make_request_handler를 재정의해야 합니다.

새로운 서비스를 정의하기 위해 필요한 작업은 위에서 설명한 내용이 전부입니다!

신규 클라이언트에 대한 처리와, 네트워크 입출력에 대한 복잡한 처리를 고민할 필요가 없습니다. Server 클래스는 Windows에서 폭넓게 적용될수 있는 IOCP를 기반으로한 Proactor구현체를 포함하며, 클라이언트의 Session관리 및 I/O 연산을 자동화 합니다.

따라서, 고성능의 서버를 제작하기 위해 Socket을 사용한 Low한 프로그래밍 작업에서 벗어나, 상위 계층에서 보다 생산적인 일에 집중할 수 있습니다.


2. Server의 선언은 다음과 같습니다.
// -*- C++ -*-

//=============================================================================
/**
 *  @file    server.h
 *
 *  $Id: server.h Fri Mon 16 17:23:23 GMT+09:00 2009
 *
 *  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
 */
//=============================================================================

#ifndef _SERVER_DEFINE
#define _SERVER_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "ace/Task.h"
#include "ace/Auto_Ptr.h"
#include "ace/INET_Addr.h"
#include "ace/Message_Block.h"
#include "ace/Atomic_Op.h"
#include "ace/Singleton.h"

#include "acceptor.h"
#include "executor.h"
#include "stream_handler.h"
#include "request_handler.h"
#include "config.h"
#include "logmgr.h"

#include <list>

#if defined(ACE_WIN32)
#include "sopt_export.h"
#elif
typedef SOPT_Export ACE_Export
#endif

class ACE_Proactor;

static int disable_signal(int sigmin, int sigmax);

namespace libsopt {

  /**
	* @class Server
	* 
	* @brief 클라이언트로 서비스를 제공하는 Server 인터페이스 클래스.
	*
	* 클라이언트로부터 요청을 처리하는 일반적인 서비스를 정의하는 인터페이스 클래스입니다.
	* Acceptor와 Executor의 구현제를 소유하며, 서비스 처리에 대한 각종 통계정보를 수집합니다.
	*
	* Server 클래스는 최소한의 작업을 통해 application-specific하게 재정의 될수 있어야 합니다.
	* application-specific하도록 서버를 재구성하기 위해서는 Request_Handler를 상속하는
	* 새로운 Request_Handler를 정의해야하며, 추상 클래스인 Server에서는 미래에 특정 목적을 가지고 작성될 
	* 새로운 Request_Handler를 알수 없기때문에, Server를 상속하는 새로운 서비스 객체에서 
	* make_request_handler를 재정의해야 합니다.
	*
	* 새로운 서비스를 정의하기 위해 필요한 작업은 위에서 설명한 내용이 전부입니다!
	*
	* 신규 클라이언트에 대한 처리와, 네트워크 입출력에 대한 복잡한 처리를 고민할 필요가 없습니다.
	* Server 클래스는 Windows에서 폭넓게 적용될수 있는 IOCP를 기반으로한 Proactor구현체를 포함하며,
	* 클라이언트의 Session관리 및 I/O 연산을 자동화 합니다.
	*
	* 따라서, 고성능의 서버를 제작하기 위해 Socket을 사용한 Low한 프로그래밍 작업에서 벗어나,
	* 상위 계층에서 보다 생산적인 일에 집중할 수 있습니다.
	*/
	class SOPT_Export Server :
		public ACE_Task_Base {
	private :

		volatile int				server_state_;

		ACE_INET_Addr				listen_;
		ACE_Proactor				*proactor_;
		ACE_Thread_Mutex			guard_;
		ACE_Auto_Ptr<Acceptor>		acceptor_;
		ACE_Auto_Ptr<Executor>		executor_;

		ACE_Atomic_Op<ACE_Thread_Mutex, size_t> client_count_, accept_count_;
		ACE_Atomic_Op<ACE_Thread_Mutex, size_t> total_request_count_, total_response_count_;
		ACE_Atomic_Op<ACE_Thread_Mutex, size_t> total_byte_to_recv_, total_byte_to_send_;

		typedef std::list<Stream_Handler *>		STREAM_HANDLER_LIST;
		typedef STREAM_HANDLER_LIST::iterator	STREAM_HANDLER_LIST_ITER;

		STREAM_HANDLER_LIST stream_handler_list_;
		
	  /**
		* Proactor I/O 디스패치 쓰레드.
		* Config 클래스의 PROACTOR_DISPATH_THREAD_COUNT필드값을 참조합니다.
		*/
		virtual int svc();
		
	  /**
		* Server에 연결되어있는 모든 클라이언트와 연결을 종료하고,
		* 리소스를 해제합니다
		*/
		void close_and_clear_all_stream_handler();
		
	  /**
		* 로그파일을 저장할 경로의 디렉토리를 생성합니다.
		*/
		void mklogdir(const char *path);

	protected :
		
		ACE_Auto_Ptr<LogMgr> logger_;
		ACE_Auto_Ptr<Config> config_;

	  /**
		* Acceptor 객체를 생성합니다.
		* 
		* @retval Acceptor * Acceptor 객체 포인터
		*/
		virtual Acceptor * make_acceptor();

	  /**
		* Executor 객체를 생성합니다.
		* 
		* @retval Executor * Executor 객체 포인터
		*/
		virtual Executor * make_executor();

	  /**
		* Proactor 객체를 생성합니다.
		* 
		* @retval ACE_Proactor * ACE_Proactor 객체 포인터
		*/
		virtual ACE_Proactor * make_proactor();

	public :
	
		/// Serve Controller 클래스.
		class Controller;
		friend class Controller;

	  /**
		* @enum SERVER_STATE
		*
		* @brief Executor의 타입
		*/
		enum SERVER_STATE { STARTING = 0x01, RUNNING, STOPPING, STOPPED };

	  /**
		* 생성자
		*
		* @param *logger LogMgr 객체 포인터 
		* @param *config Config 객체 포인터
		*/
		Server(LogMgr *logger, Config *config)
			: server_state_(STOPPED),
			proactor_(0), acceptor_(0), executor_(0), client_count_(0), accept_count_(0),
			logger_(logger), config_(config)
		{ /* empty */ }

		/// 기본소멸자
		virtual ~Server()
		{ /* empty */ }

		/// 서비스를 시작합니다.
		int run_server_process();

		/// 서비스를 종료합니다.
		int end_server_process();

	  /**
		* Server의 실행상태를 확인합니다.
		* 
		* @param state 확인할 상태
		*
		* @retval 1 Server는 state 상태에 있습니다. 
		* @retval 0 Server는 state 상태에 있지 않습니다.
		*/
		int check_server_state(SERVER_STATE state) const;

	  /**
		* Server의 실행상태를 변경합니다.
		* 
		* @param to_ch_state 변경할 상태
		*/
		void change_server_state(SERVER_STATE to_ch_state);

	  /**
		* 클라이언트의 연결완료 이벤트 통지 메소드
		* 
		* @param to_ch_state *h 신규 서비스 핸들러 객체 포인터
		*/
		virtual void on_connection_established(Stream_Handler *h);

	  /**
		* 클라이언트의 연결종료 이벤트 통지 메소드
		* 
		* @param to_ch_state *h 연결을 종료할 서비스 핸들러 객체 포인터
		*/
		virtual void on_connection_closed(Stream_Handler *h);

	  /**
		* Acceotor의 make_handler 메소드를 통해 호출됩니다.
		* 기본구현은 ::operator new를 사용해 객체를 생성합니다.
		* 별도의 Allocator/Deallocator를 구현하여 사용할수 있습니다.
		* 
		* @retval Stream_Handler * 신규 서비스 핸들러 객체 포인터
		*/
		virtual Stream_Handler * make_stream_handler();

	  /**
		* make_stream_handler를 통해 생성된 객체를 해제합니다.
		* 기본구현은 ::operator delete를 사용해 객체를 소멸시킵니다.
		* 별도의 Allocator/Deallocator를 구현하여 사용할수 있습니다.
		* 
		* @param Stream_Handler * 리소스를 해제할 서비스 핸들러 객체 포인터
		*/
		virtual void release_stream_handler(Stream_Handler *h);

	  /**
		* Request_Hadler 객체를 생성합니다.
		* 기본구현은 ::operator new를 사용해 객체를 생성합니다.
		* 별도의 Allocator/Deallocator를 구현하여 사용할수 있습니다.
		* Server를 상속하는 클래스는 반드시 메소드를 구현해야 합니다.
		* 
		* @retval Request_Handler * Request_Hadler 객체 포인터
		*/
		virtual Request_Handler * make_request_handler() = 0;

	  /**
		* make_request_handler통해 생성된 객체를 해제합니다.
		* 기본구현은 ::operator delete를 사용해 객체를 소멸시킵니다.
		* 별도의 Allocator/Deallocator를 구현하여 사용할수 있습니다.
		* Server를 상속하는 클래스는 반드시 메소드를 구현해야 합니다.
		* 
		* @param Request_Handler * 리소스를 해제할 Request_Handler 객체 포인터
		*/
		virtual void release_request_handler(Request_Handler *r) = 0;

	  /**
		* Request_Handler에 정의된 Header와 Payload가 정상적으로 수신되었을때,
		* 호출되는 이벤트 통지 메소드.
		* 수신된 Request_Handler가 urgent_request라면 즉시 처리되며,
		* 그렇지 않다면면 Executor로 전달됩니다.
		* Executor로는 Stream_Handler 객체 포인터와 Request_Handler 객체 포인터 모두
		* 전달되며, 특히 Stream_Handler 객체는 RefCount가 1증가합니다.
		*
		* @param Stream_Handler * Request_Handler를 수신한 서비스 핸들러 객체 포인터
		* @param Request_Handler * 수신된 Request_Handler 객체 포인터
		*/
		virtual void stream_read_complete(Stream_Handler *h, Request_Handler *r);

	  /**
		* 클라이언트로 데이터 전송이 완료되었을때 호출되는 이벤트 통지 메소드
		* 
		* @param Stream_Handler * 데이터를 전송한 서비스 핸들러 객체 포인터
		* @param Request_Handler * 전송이 완료된 Request_Handler 객체 포인터
		*/
		virtual void stream_write_complete(Stream_Handler *h, Request_Handler *r);

		size_t current_client_count() const
		{ return client_count_.value(); }

		void increse_current_client_count()
		{ ++client_count_; }

		void decrese_current_client_count()
		{ if( client_count_ > 0 ) --client_count_; }

		void increse_accept_count()
		{ ++accept_count_; }

		size_t total_request_count() const
		{ return total_request_count_.value(); }

		void increse_total_request_count()
		{ ++total_request_count_; }

		size_t total_response_count() const 
		{ return total_response_count_.value(); }

		void increse_total_response_count()
		{ ++total_response_count_; }

		size_t total_byte_to_recv() const
		{ return total_byte_to_recv_.value(); }

		void increse_total_byte_to_recv(size_t byte_to_recv)
		{ total_byte_to_recv_ += byte_to_recv; }

		size_t total_byte_to_send() const
		{ return total_byte_to_send_.value(); }

		void increse_total_byte_to_send(size_t byte_to_send)
		{ total_byte_to_send_ += byte_to_send; }

		Config * config() const
		{ return config_.get(); }
	};

  /**
	* @class Server::Controller
	* 
	* @brief Server를 제어하는 Controller 클래스.
	*
	* 로컬 또는 원격지의 서버를 제어하기 위한 Controller 인터페이스 입니다.
	* Controller의 기본구현은 콘솔입력을 받아들이며, 서버를 제어합니다.
	* 콘솔에 exit를 입력해 Server를 종료시킬수 있습니다.
	*/
	class SOPT_Export Server::Controller :
		public ACE_Task_Base {
	protected :
		
		/// Controller가 제어할 Server 객체 포인터
		Server *serv_;

	  /**
		* Controller의 상세구현을 포함하는 메소드이며, Active Object 패턴을 통해 실행됩니다.
		* 기본 구현은 Config 객체의 LOGGING_LEVEL필드와 LOGGING_OUTPUT_TYPE필드를 이용하여,
		* LogMgr의 초기화하며, 이후 콘솔입력을 처리합니다.
		* 
		* 원격지를 통한 Server 제어를 위해서는 Server::Controller를 상속하는 application-specific한
		* Controller를 이용할수 있습니다.
		* 
		* 해당 메소드가 return되면, Controller와 Server는 순차적으로 종료합니다. 
		*/
		virtual int svc();

	public :
		
		/// 기본생성자
		explicit Controller(Server *serv)
			: serv_(serv)
		{ /* empty */ }

		/// Controller를 시작합니다.
		int run_server_controller_process()
		{ return this->activate(); }
	};

};

#endif

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 16:06
1. 개요
Executor 인터페이스 클래스.
송신된 Request_Handler task를 실행하는 인터페이스입니다. Executor을 상속하는 구현체는 각 task의 실행방식이나 스케쥴링방식을 구현합니다.

Executor 인터페이스는 task의 실행방식이나 스케쥴링방식으로부터 task의 전달을 분리하는 방법을 제공합니다. 예를들어, 서비스의 상제 구현을 담고 있는 call 메소드를 포함하는 Requet_Handler의 call 메소드를 직접호출하는 대신에, 아래와 같은 코드를 사용할수 있습니다.

Executor executor = new Single_Executor;
executor.execute(new Request_Handler()); 

다만, Executor 인터페이스에서 실행되는 task는 쓰레드 스케쥴링의 불규칙성으로 인하여 실행의 순서를 예측할수 없으며, 순서가 보장되어야 하는 task는 Executor를 상속하는 구현체중 하나인 Single_Executor를 이용한다면, 적어도 Executor에 전송되는 순서에 따라 FIFO 구조를 통해 순서실행보장을 받을수 있습니다.

Executor의 구현체는 Single_Executor, Fixed_Executor, LF_Executor가 있습니다. Single_Executor는 제외하고는, 모두 쓰레드풀을 사용하게 되며, 쓰레드풀의 크기는 Config객체의 EXECUTORS_DISPATH_THREAD_COUNT필드의 값입니다. Single_Executor의 경우 EXECUTORS_DISPATH_THREAD_COUNT필드의 값은 무시되며 쓰레드풀의 크기는 1 입니다.


2. Executor의 선언은 다음과 같습니다.
// -*- C++ -*-

//=============================================================================
/**
 *  @file    executor.h
 *
 *  $Id: executor.h Fri Mon 16 17:23:23 GMT+09:00 2009
 *
 *  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
 */
//=============================================================================

#ifndef _EXECUTOR_DEFINE
#define _EXECUTOR_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "ace/Task.h"
#include "ace/Synch.h"
#include "ace/Activation_Queue.h"
#include "ace/Method_Request.h"

#if defined(ACE_WIN32)
#include "sopt_export.h"
#elif
typedef SOPT_Export ACE_Export
#endif

namespace libsopt {

	class Server;
	class Request_Handler;

  /** 
	* @class Executor
	*
	* @brief Executor 인터페이스 클래스.
	*
	* 송신된 Request_Handler task를 실행하는 인터페이스입니다.
	* Executor을 상속하는 구현체는 각 task의 실행방식이나 스케쥴링방식을 구현합니다.
	*
	* Executor 인터페이스는 task의 실행방식이나 스케쥴링방식으로부터 task의 전달을
	* 분리하는 방법을 제공합니다.
	* 예를들어, 서비스의 상제 구현을 담고 있는 call 메소드를 포함하는 Requet_Handler의 
	* call 메소드를 직접호출하는 대신에, 아래와 같은 코드를 사용할수 있습니다.
	* 
	* Executor executor = new Single_Executor;
	* executor.execute(new Request_Handler());
	* ...
	*
	* 다만, Executor 인터페이스에서 실행되는 task는 쓰레드 스케쥴링의 불규칙성으로 인하여 
	* 실행의 순서를 예측할수 없으며, 순서가 보장되어야 하는 task는 Executor를 상속하는 구현체중 하나인 
	* Single_Executor를 이용한다면, 적어도 Executor에 전송되는 순서에 따라 FIFO 구조를 통해 
	* 순서실행보장을 받을수 있습니다.
	*
	* Executor의 구현체는 Single_Executor, Fixed_Executor, LF_Executor가 있습니다.
	* Single_Executor는 제외하고는, 모두 쓰레드풀을 사용하게 되며, 쓰레드풀의 크기는 Config객체의
	* EXECUTORS_DISPATH_THREAD_COUNT필드의 값입니다.
	* Single_Executor의 경우 EXECUTORS_DISPATH_THREAD_COUNT필드의 값은 무시되며 쓰레드풀의 크기는 1 입니다.
	*/
	class SOPT_Export Executor :
		public ACE_Task<ACE_MT_SYNCH> {
	protected :

		/// Executor를 소유하는 Server 객체에 대한 포인터
		Server *serv_;
		
		/// 송신된 task를 저장하는 Queue
		ACE_Activation_Queue	request_q_;

		/// Executor 타입
		int						executor_type_;

		/// Executor 실행상태
		volatile int			executor_state_;
		
		/// Executor를 상속하는 구현체를 위한 인터페이스 메소드
		virtual int svc() = 0;

	  /**
		* task가 실행된 이후, 리소스 정리를 위해 호출됩니다.
		* Executor에서 사용되는 모든 리소스는 RefCount기법을 이용하며,
		* 해당 메소드가 호출되면 task와 관련된 모든 리소의 RefCount에서 1이 감소합니다.
		* RefCount가 0이된다면, 리소스는 해제될 것입니다.
		*
		* 관련 리소스에 대한 항목은 Request_Handler의 clear 메소드와
		* Stream_Hanlder의 clear 메소드가 참고하기 바랍니다.
		*
		* @param r task 객체 포인터
		* @param retval task 객체의 call메소드 반환값. retvalue이 0이 아니라면, task를 전송한
		* Stream_Handler과 연결을 해제할 것입니다.
		*/
		void execute_done(Request_Handler *r, int retvalue);

	public :
		
	  /**
		* @enum EXECUTOR_TYPE
		*
		* @brief Executor의 타입
		*/
		enum EXECUTOR_TYPE { SINGLE_EXECUTOR = 0x01, FIXED_EXECUTOR, LEADER_FOLLOWER_EXECUTOR };

	  /**
		* @enum EXECUTOR_STATE
		*
		* @brief Executor의 동작상태
		*/
		enum EXECUTOR_STATE { RUNNING = 0x01, STOPPING, STOPPED };

	  /**
		* Executor 객체를 생성합니다.
		*
		* @param executor_type Executor 구현체의 타입
		*/
		explicit Executor(EXECUTOR_TYPE executor_type)
			: request_q_(this->msg_queue()), executor_type_(executor_type), executor_state_(STOPPED)
		{ request_q_.queue()->high_water_mark(ACE_INT32_MAX); }

		/// 기본소멸자
		virtual ~Executor()
		{ /* empty */ }

		/// Executor를 시작합니다.
		int run_executor_process();

		/// Executor를 정지합니다.
		int end_executor_process();

	  /**
		* task를 Executor로 전송합니다.
		*
		* @param r task 객체 포인터
		* @param timeout task전송이 블록된다면, timeout만큼 대기합니다.
		*
		* @retval 0 성공.
		* @retval -1 실패: errno를 확인할수 있습니다.
		*		- EWOULDBLOCK: timeout
		*		- ESHUTDOWN: Executor는 task를 수신할수 없습니다.
		*/
		int exexute(ACE_Method_Request *r, ACE_Time_Value *timeout = 0);

		/// Executor에 보관중인 모든 task를 해제합니다.
		void clear();

	   /**
		* Executor의 실행상태를 확인합니다.
		* 
		* @param state 확인할 상태
		*
		* @retval 1 Executor는 state 상태에 있습니다. 
		* @retval 0 Executor는 state 상태에 있지 않습니다.
		*/
		int check_executor_state(EXECUTOR_STATE state) const;

	  /**
		* Executor의 실행상태를 변경합니다.
		* 
		* @param to_ch_state 변경할 상태
		*/
		void change_executor_state(EXECUTOR_STATE to_ch_state);

		///  return serv_;
		Server *server() const
		{ return serv_; }

		/// serv_ = serv;
		void server(Server *serv)
		{ serv_ = serv; }
	};
};

#endif


'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 16:04
1. 개요
Request_Handler 인터페이스 클래스.
클라이언트의 Request를 추상화하는 인터페이스입니다. 모든 Request는 Header, Payload 구조를 지니게 됩니다.

Header: urgent_request, header_length, payload_length를 반드시 포함해야합니다.

urgent_request: 해당 Request가 Executor로 전달되어 처리되어야 하는지, Executor에 전달되지 않고 바로 처리되어야 하는지를 나타냅니다. Request_Handler를 상속하는 구현체는 urgent_request 메소드를 반드시 구현해야하며, 메소드가 0이 아닌값을 반환한다면, 해당 Request는 Executor에 전달되지 않고 바로 처리됩니다.
header_length: Header의 길이를 나타냅니다. Request_Handler를 상속하는 구현체는header_length 메소드를 반드시 구현해야 합니다.
payload_length: Header뒤에 따라오는 payload의 데이터 길이를 나타냅니다. Request_Handler를 상속하는 구현체는payload_length 메소드를 반드시 구현해야 합니다.
Payload: Header의 뒤에 따라오는 application-specific한 데이터 입니다. Header의 payload_length를 이용해 payload의 크기를 알수 있습니다.

또한, equest_Handler를 상속하는 구현체 call 메소드를 반드시 구현해야 합니다. call 메소드는 application-specific한 서비스 로직을 표현합니다. Executor에 의해 실행되거나, urgent_request가 0이 아닌값을 반환한다면 Executor에 전달되지 않으며, 즉시, call 메소드를 호출하게 됩니다.

Request_Handler는 RefCount기법을 이용해 생명주기를 관리합니다. 모든 Request_Handler 객체는 반드시 사용이 끝난 후 clear 메소드를 올바르게 호출해야 합니다. Executor에 의해 Request_Handler가 실행되었다면, Executor의 execute_done 메소드에서 clear 메소드의 호출을 보장합니다.


2. Request_Handler의 선언은 다음과 같습니다.
// -*- C++ -*-

//=============================================================================
/**
 *  @file    request_handler.h
 *
 *  $Id: request_handler.h Fri Mon 16 17:23:23 GMT+09:00 2009
 *
 *  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
 */
//=============================================================================

#ifndef _REQUEST_HANDLER_DEFINE
#define _REQUEST_HANDLER_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "ace/Method_Request.h"
#include "ace/Message_Block.h"

#if defined(ACE_WIN32)
#include "sopt_export.h"
#elif
typedef SOPT_Export ACE_Export
#endif

namespace libsopt {

	class Server;
	class Stream_Handler;

  /**
	* @class Request_Handler
	* 
	* @brief Request_Handler 인터페이스 클래스
	*
	* 클라이언트의 Request를 추상화하는 인터페이스입니다.
	* 모든 Request는 Header, Payload 구조를 지니게 됩니다.
	*
	* Header: urgent_request, header_length, payload_length를 반드시 포함해야합니다. 
	*	- urgent_request: 해당 Request가 Executor로 전달되어 처리되어야 하는지, 
	*	Executor에 전달되지 않고 바로 처리되어야 하는지를 나타냅니다.
	*	Request_Handler를 상속하는 구현체는 urgent_request 메소드를 반드시 구현해야하며,
	*	메소드가 0이 아닌값을 반환한다면, 해당 Request는 Executor에 전달되지 않고 바로 처리됩니다.
	*	- header_length: Header의 길이를 나타냅니다.
	*	Request_Handler를 상속하는 구현체는header_length 메소드를 반드시 구현해야 합니다.
	*	- payload_length: Header뒤에 따라오는 payload의 데이터 길이를 나타냅니다.
	*	Request_Handler를 상속하는 구현체는payload_length 메소드를 반드시 구현해야 합니다.
	*
	* Payload: Header의 뒤에 따라오는 application-specific한 데이터 입니다.
	* Header의 payload_length를 이용해 payload의 크기를 알수 있습니다.
	* 
	* 또한, equest_Handler를 상속하는 구현체 call 메소드를 반드시 구현해야 합니다.
	* call 메소드는 application-specific한 서비스 로직을 표현합니다.
	* Executor에 의해 실행되거나, urgent_request가 0이 아닌값을 반환한다면 Executor에 전달되지 않으며,
	* 즉시, call 메소드를 호출하게 됩니다.
	*
	* Request_Handler는 RefCount기법을 이용해 생명주기를 관리합니다.
	* 모든 Request_Handler 객체는 반드시 사용이 끝난 후 clear 메소드를 올바르게 호출해야 합니다.
	* Executor에 의해 Request_Handler가 실행되었다면, Executor의 execute_done 메소드에서 clear 메소드의
	* 호출을 보장합니다.
	*/
	class SOPT_Export Request_Handler :
		public ACE_Method_Request, 
		public ACE_Message_Block {
	protected :

		/// Request_Handler를 생성한 Server 객체에 대한 포인터
		Server *serv_;

		/// Request_Handler를 전송한 Stream_Handler 객체에 대한 포인터
		Stream_Handler *stream_handler_;

	public :

		/// 기본생성자
		explicit Request_Handler(Stream_Handler *h = 0)
			: stream_handler_(h)
		{ /* empty */ }

		/// 기본소멸자
		virtual ~Request_Handler()
		{ /* empty */ }

		/// Request_Handler를 초기화 합니다.
		int open();

		/// Request_Handler의 RefCount를 1 감소시킵니다.
		void clear();

		/// Stream_Handler의 RefCount를 1 감소시킵니다.
		void stream_handler_clear(); 

		/// Header의 크기를 반환합니다.
		/// Request_Handler는 상속하는 규현체는 해당 메소드를 반드시 구현해야합니다.
		virtual size_t header_length() = 0;

		/// Payload의 크기를 반환합니다.
		/// Request_Handler는 상속하는 규현체는 해당 메소드를 반드시 구현해야합니다.
		virtual size_t payload_length() = 0;

		/// Urgent Request를 구별합니다.
		/// Request_Handler는 상속하는 규현체는 해당 메소드를 반드시 구현해야합니다.
		virtual int urgent_request() = 0;

		/// application-specific한 서비스 로직 입니다.
		/// Request_Handler는 상속하는 규현체는 해당 메소드를 반드시 구현해야합니다.
		virtual int call() = 0;

	   /**
		* Request_Handler parsing 헬퍼 메소드
		* offset으로부터 length의 길이반큼 바이트를 읽어 T로 변환하여 반환합니다.
		*
		* @param T 변환할 타입
		* @param offset 기준점
		* @param length 바이트길이
		*/
		template<typename T>
		T read_data(int offset, int length = sizeof T)
		{
			T *data = (T*)(this->base() + offset);
			return *data;
		}

		/// stream_handler_ = h;
		void stream_handler(Stream_Handler *h)
		{ stream_handler_ = h; }

		/// return stream_handler_;
		Stream_Handler * stream_handler()
		{ return stream_handler_; }

		/// return serv_;
		Server *server() const
		{ return serv_; }

		/// serv_ = serv;
		void server(Server *serv)
		{ serv_ = serv; }
	};

};

#endif

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 16:02
1. 개요
Stream_Handler 신규 서비스 요청에 대한 종단점를 나타내는 클래스.
신규 서비스 요청의 인증이 완료된 이후 Accepor의 make_handler 메소드를 통해 생성되며, 신규 서비스 요청에 대한 종단점(이하, 클라이언트)를 처리하기 위한 클래스입니다.

클라이언트와의 메세지 송수신은 asynchronously I/O를 이용하도록 디자인되었습니다. Windows에서는 IOCP를 이용한 Proactor 패턴이 사용되며, POSIX 호환 플랫폼에서는 POSIX Realtime signal 을 이용한 io control block을 사용되여 Proactor 패턴이 구현됩니다.

Stream_Handler는 일반적으로 Request_Handler와 함께 사용되어야 합니다. Request_Handler는 클라이언트로부터 전송되어오는 데이터를 사용하기 편리하도록 캡슐화합니다.

Request_Handler가 올바르게 수신되었다면, Executor로 Stream_Handler와 함께 전달됩니다.

클라이언트의 예기치 않은 종료로 인해, 이미 Executor로 전달된 task에서 발생될수 있는 예외의 올바른 처리를 위해 RefCount기법을 이용합니다.

즉, 클라이언트와의 연결은 종료되지만 Stream_Handler 객체의 소멸은 Executor에 존재하는 해당 Stream_Handler가 모두 처리될때까지 지연됩니다.


2. Stream_Handler의 선언은 다음과 같습니다.
// -*- C++ -*-

//=============================================================================
/**
 *  @file    stream_handler.h
 *
 *  $Id: stream_handler.h Fri Mon 16 17:23:23 GMT+09:00 2009
 *
 *  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
 */
//=============================================================================

#ifndef _STREAM_HANDLER_DEFINE
#define _STREAM_HANDLER_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "ace/Synch.h"
#include "ace/Asynch_IO.h"
#include "ace/INET_Addr.h"
#include "ace/Refcountable.h"

#if defined(ACE_WIN32)
#include "sopt_export.h"
#elif
typedef SOPT_Export ACE_Export
#endif

namespace libsopt {

	class Server;
	class Request_Handler;

  /**
	* @class Stream_Handler
	* 
	* @brief Stream_Handler 신규 서비스 요청에 대한 종단점를 나타내는 클래스.
	*
	* 신규 서비스 요청의 인증이 완료된 이후 Accepor의 make_handler 메소드를 통해 생성되며,
	* 신규 서비스 요청에 대한 종단점(이하, 클라이언트)를 처리하기 위한 클래스입니다.
	*
	* 클라이언트와의 메세지 송수신은 asynchronously I/O를 이용하도록 디자인되었습니다.
	* Windows에서는 IOCP를 이용한 Proactor 패턴이 사용되며, POSIX 호환 플랫폼에서는 POSIX Realtime signal
	* 을 이용한 io control block을 사용되여 Proactor 패턴이 구현됩니다.
	*
	* Stream_Handler는 일반적으로 Request_Handler와 함께 사용되어야 합니다.
	* Request_Handler는 클라이언트로부터 전송되어오는 데이터를 사용하기 편리하도록 캡슐화합니다.
	*
	* Request_Handler가 올바르게 수신되었다면, Executor로 Stream_Handler와 함께 전달됩니다.
	*
	* 클라이언트의 예기치 않은 종료로 인해, 이미 Executor로 전달된 task에서 발생될수 있는 예외의 올바른 처리를 위해
	* RefCount기법을 이용합니다.
	*
	* 즉, 클라이언트와의 연결은 종료되지만 Stream_Handler 객체의 소멸은 Executor에 존재하는 해당 Stream_Handler가
	* 모두 처리될때까지 지연됩니다.
	*/
	class SOPT_Export Stream_Handler :
		public ACE_Service_Handler, 
		public ACE_Refcountable {
	protected :
	
		/// Stream_Handler를 생성한 Server 객체에 대한 포인터
		Server *serv_;
		
		ACE_Asynch_Read_Stream		reader_;
		ACE_Asynch_Write_Stream		writer_;
		ACE_Thread_Mutex			guard_;
		ACE_INET_Addr				remote_address_;

		int	closemark_;
		int	read_shutdown_;
		int	write_shutdown_;

		void shutdown(int sd_type);

		void abortive_close();

		/// 클라이언트와의 연결을 종료합니다.
		/// 연결이 종료된 후, Server로 연결종료를 통지합니다.
		void connection_close();
	
	public :
		
		/// 기본생성자
		explicit Stream_Handler()
			: ACE_Refcountable(0), closemark_(1), read_shutdown_(0), write_shutdown_(0)
		{ /* empty */ }

		/// 기본소멸자.
		/// 클라이언트와의 연결이 유효하다면 연결을 종료합니다.
		virtual ~Stream_Handler();

	   /**
		* 신규 서비스 요청의 인증이 완료된 이후 Accepor의 make_handler 메소드를 통해 생성된 직후,
		* 
		* @param new_handle 신규 종단점애 대한 핸들러
		* @param message_block 사용되지 않음
		*/
		virtual void open(ACE_HANDLE new_handle, ACE_Message_Block &message_block);

	  /**
		* Executor에 전달되기 전에, RefCount를 1증가 합니다.
		* 
		* @retval Stream_Handler * this 포인터
		*/
		Stream_Handler * shallow_copy();
		
	  /**
		* Stream_Handler의 RefCount를 1감소 합니다.
		* RefCount가 0이 된다면 해당 객체를 소멸시킵니다.
		* 또한, closemark_ 필드를 확인하여, 클라이언트와의 연결을 종료할수 있습니다.
		*/
		void clear();

	  /**
		* 예기치 않은 오류나, call 메소드의 부정확한 실행결과, 기타상황으로 인해
		* 클라이언트와의 연결을 종료해야 한다면 0이 아닌 값으로 mark를 전달합니다.
		*
		* @param mark mark가 0이 아닌 값이라먄, clear메소드 호출시 해당 클라이언트와의 연결을 종료합니다.
		*/
		void closemark(int mark);

		/// closemark_ 필드의 값을 반환합니다.
		int closemark();

	  /** 
	    * 클라이언트의 핸들의 유효성을 검증합니다.
		* 
		* @retval 0 클라이언트 핸들이 유효하지 않습니다.
		* @retval 1 클라이언트의 핸들이 유요합니다.
		*/
		int has_valid_handle();

	  /** 
		* how에 의해 결정되는 클라이언트와의 연결상태를 검증합니다.
		* 
		* @param how
		*		- ACE_SHUTDOWN_READ
		*		- ACE_SHUTDOWN_WRITE
		*		- ACE_SHUTDOWN_BOTH
		*
		* @retval 0 클라이언트와의 연결이 유효하지 않습니다.
		* @retval 1 클라이언트와의 연결이 유요합니다.
		*/
		int is_connected(int how);

	  /** 
		* Request_Handler의 Header를 수신을 바인딩합니다.
		* 
		* @retval 0 바인딩을 성공했습니다.
		* @retval -1 바인딩을 실패했습니다.
		*/
		int handle_read_header();

	  /** 
		* Request_Handler의 Payload를 수신을 바인딩합니다.
		* 
		* @param r Header 수신을 완료한 Request_Handler 입니다.
		*
		* @retval 0 바인딩을 성공했습니다.
		* @retval -1 바인딩을 실패했습니다.
		*/
		int handle_read_payload(Request_Handler *r);

	  /** 
		* synchronously send
		*
		* @param write_mb 전송할 메세지
		* @param length 전송할 메세지의 길이
		*/
		void synch_write_n(ACE_Message_Block *write_mb, ssize_t length);

	  /** 
		* asynchronously send의 작업 완료통지 메소드
		*
		* @param write_mb 전송할 메세지
		* @param length 전송할 메세지의 길이
		*/
		void asynch_write_n(ACE_Message_Block *write_mb, ssize_t length);

		/// 신규종단점에 대한 IPv4주소를 수집합니다.
		virtual void 
			addresses(const ACE_INET_Addr &remote_address, const ACE_INET_Addr &local_address);

	  /** 
		* asynchronously read의 작업 완료통지 메소드
		*
		* @param result 작업완료에 대한 결과정보
		*/
		virtual void 
			handle_read_stream(const ACE_Asynch_Read_Stream::Result &result);

	  /** 
		* asynchronously write의 작업 완료통지 메소드
		* 
		* @param result 작업완료에 대한 결과정보
		*/
		virtual void 
			handle_write_stream(const ACE_Asynch_Write_Stream::Result &result);

		/// 클라이언트의 IPv4 주소를 문자열로 반환합니다.
		const char * remote_addr_by_string();
		
		/// 클라이언트의 IPv4 주소를 32bit 정수형으로 반환합니다.
		ACE_UINT32 remote_addr_by_integer();
		
		/// 클라이언트의 포트번호를 반환합니다.
		u_short remote_port();

		/// return serv_;
		Server * Stream_Handler::server() const
		{ return serv_; }

		/// serv_ = serv;
		void Stream_Handler::server(Server *serv)
		{ serv_ = serv; }
	};

};

#endif

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 15:58
1. 개요
신규 서비스 핸들러(Stream_Handler)를 생성하기 위한 factory 클래스.
클라이언트의 능동적인(passively) 연결요청을 처리하기 위한 클래스 입니다. Acceptor는 Stream_Handler 객체 또는Stream_Handler 클래스를 상속하는 application-specific한 Stream 객체를 make_handler 메소드를 이용해 생성합니다.

일반적으로 make_handler 메소드에서는 Server 클래스의 make_stream_handler 메소드를 호출하며, Server 클래스를 상속하는 application-specific한 Server 객체에서는 별도의 Allocator를 이용할수 있습니다. 기본구현에서는 operator new를 이용해 객체를 생성합니다.

또한, 신규 서비스 요청을 validate_connection 메소드를 이용해 검증할 수 있으며, 해당 메소드가 -1을 반환시 신규 서비스 요청은 더이상 처리되지 않고, 연결이 종료됩니다.

Acceptor는 asynchronously 하도록 디자인 되었습니다.


2. Acceptor의 선언은 다음과 같습니다.
// -*- C++ -*-

//=============================================================================
/**
 *  @file    acceptor.h
 *
 *  $Id: acceptor.h Fri Mon 16 17:23:23 GMT+09:00 2009
 *
 *  @author Copyright 2008-2009 SOPT(Shout Our Passion Together)
 */
//=============================================================================

#ifndef _ACCEPTOR_DEFINE
#define _ACCEPTOR_DEFINE

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif

#include "ace/Asynch_Acceptor.h"
#include "stream_handler.h"

#if defined(ACE_WIN32)
#include "sopt_export.h"
#elif
typedef SOPT_Export ACE_Export
#endif

namespace libsopt {

	class Server;

  /**
	* @class Acceptor
	*
	* @brief 신규 서비스 핸들러(Stream_Handler)를 생성하기 위한 factory 클래스.
	*
	* 클라이언트의 능동적인(passively) 연결요청을 처리하기 위한 클래스 입니다.
	* Acceptor는 Stream_Handler 객체 또는 Stream_Handler 클래스를 상속하는 application-specific한
	* Stream 객체를 make_handler 메소드를 이용해 생성합니다.
	* 
	* 일반적으로 make_handler 메소드에서는 Server 클래스의 make_stream_handler 메소드를 호출하며,
	* Server 클래스를 상속하는 application-specific한 Server 객체에서는 별도의 Allocator를 이용할수 있습니다.
	* 기본구현에서는 ::operator new를 이용해 객체를 생성합니다.
	* 
	* 또한, 신규 서비스 요청을 validate_connection 메소드를 이용해 검증할 수 있으며, 
	* 해당 메소드가 -1을 반환시 신규 서비스 요청은 더이상 처리되지 않고, 연결이 종료됩니다.
	* 
	* Acceptor는 asynchronously 이용하도록 디자인 되었습니다.
	*/
	class SOPT_Export Acceptor : 
		public ACE_Asynch_Acceptor<Stream_Handler> {
	protected :
		
		/// Acceptor를 소유하는 Server 객체에 대한 포인터
		Server *serv_;

		/// Acceptor가 바인딩된 IPv4 주소
		ACE_INET_Addr listen_addr_;
		
	  /**
		* Stream_Handler 객체를 생성합니다. 객체의 생성방법은
		* application-specific하게 재지정될수 있으며, 일반적으로 Server::make_stream_handler
		* 메소드를 이용합니다.
		*
		* @retval Stream_Handler * 신규 서비스를 나타내는 Stream_Handler 객체의 포인터
		* @retval 0 Stream_Handler 객체 생성 실패
		*/
		virtual Stream_Handler * make_handler();

	  /**
		* 신규 서비스 요청에 대한 인증작업을 수행합니다.
		* 기본인증작업은 Config 객체에 정의된 MAX_USER_COUNT을 초과하지 않는 범위내에서
		* 신규 서비스 요청을 받아들입니다. MAX_USER_COUNT을 초과할 경우 신규 서비스 요청은
		* 더이상 받아들여지지 않습니다.
		* 
		* @param result asynchronously result 객체
		* @param remote 신규 서비스의 종단점을 나타내는 IPv4 주소
		* @param local Acceptor의 IPv4 주소 
		*
		* @retval 0 성공
		* @retval -1 실패. 신규 서비스 요청은 즉시 정지되며, 연결이 해제됩니다.
		*/
		virtual int 
			validate_connection(const ACE_Asynch_Accept::Result& result, 
			const ACE_INET_Addr &remote, 
			const ACE_INET_Addr &local);
		
		/// 동작중인 Acceptor를 정지시키며, 관련 리소스를 해제합니다.
		virtual int cancel() ;

	public :
	
		/// 기본소멸자
		virtual ~Acceptor() 
		{ /* empty */ }
		
		/// Acceptor를 시작합니다.
		int run_acceptor_process();

		/// Acceptor를 정지합니다.
		int end_acceptor_process();

		///  return serv_;
		Server *server() const
		{ return serv_; }

		/// serv_ = serv;
		void server(Server *serv)
		{ serv_ = serv; }
	};

};

#endif

'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop
TAG ACE, c++, libSOPT

댓글을 달아 주세요

프로젝트/libSOPT2010.03.24 11:54
본 프로젝트는 오랜 기간 다양한 분야에서 개발되어 온 공개 네트워크 라이브러리인 ACE를 사용하여 서버 프레임워크를 구성하는 것을 목표로 합니다. 서버 프레임워크는 다음과 같은 네 가지 사항을 고려하여 디자인 되었습니다.

  • 중복되는 기능에 대한 반복 작업을 최소화한다.
  • 핵심 기능의 모듈화를 통해 확장성은 증가시키고, 복잡도는 최소화한다.
  • 프레임워크 이용자는 TCP통신과 같은 하위 계층은 작업은 신경쓰지 않고, 서비스 로직과 같은 보다 생산적인 일에 집중할수 있어야 한다.
  • 검증된 라이브러리를 사용한다.

위와 같은 요구사항을 충족시키기 위해 사용된 ACE라이브러리는 동시 처리 방식의 많은 핵심 패턴들을 구현하고 있는 객체 지향 툴입니다. 특히 ACE는 고성능 실시간 통신 서비스와 어플리케이션을 개발하는 데 많은 기능을 제공합니다. 이것은 프로세스간 통신, 이벤트 다중 수신, 명시적 동적 링킹, 동시 처리 방식의 객체지향 네트워크 어플리케이션과 서비스의 개발을 단순하게 만들어 줍니다.


ACE의 장점을 요약하면 다음과 같습니다.

이식성의 증가.
일반적으로 네트워크 어플리케이션과 같이 운영체제와 밀접한 어플리케이션을 다른 운영체제로 이식하는 작업은 서로 비슷한 특성을 지닌 운영체제 위에서라도 많은 시간과 노력이 필요할수 있으며, 서로 상이한 특성한 지닌 운영체제 위에서라면 거의 불가능한 작업입니다. 하지만 ACE 컴포넌트들은 하나의 운영체제 위에서 동시처리방식의 네트워크 어플리케이션을 작성하기 쉽게 해주는것은 물론, 수많은 다른 운영체제로의 이식하는 작업을 손쉽게 해줍니다.

소프트웨어 품질의 증가.
ACE 컴포넌트는 통신 소프트웨어의 핵심 품질요소 즉, 융통성, 확장성, 재사용성, 모듈화 등을 향상시키기 위해 POSA2에서 제안하는 많은 핵심 패턴을 사용해서 디자인되었습니다.

효율과 예측력의 증가.
ACE는 넓은 분야의 어플리케이션과 QoS 요구를 지원하기 위해 조심스레 디자인되었습니다. 이런 요구에는 딜레이에 민감한 어플리케이션을 위한 낮은 지연, 대역폭에 민감한 어플리케이션을 위한 높은 성능, 실시간 어플리케이션을 위한 예측 능력 등이 있습니다.

ACE는 The ACE ORB(TAO)에서 사용된 재사용가능한 컴포넌트와 패턴들을 제공한다.
TAO는 실시간 시스템과 고성능에 최적화된 CORBA의 오픈소스 표준을 따르는 구현물입니다. 따라서 협력적인 미들웨어 솔루션을 제공할 목적으로 ACE와 TAO는 서로 궁합이 잘 맞도록 구현되어있습니다.


위와 같은 ACE를 기반으로 libSOPT는 서버 어플리케이션을 구축하기 위한 4개의 핵심 컴포넌트와, 이들을 통합하는 Server 컴포넌트(http://lyb1495.tistory.com/entry/libSOPTServer)로 구성되어 있습니다. 컴포넌트의 구성은 다음과 같습니다.


프레임워크는 의도적으로 미완성된 소프트웨어를 말합니다. libSOPT 역시 마찬가지 입니다. 범용적인 사용 목적을 위해 디자인된 libSOPT는 각각의 목적에 따라 커스터마이징이 가능하도록 설계된 소프트웨어로서, 커스터마이징하기 위한 인터페이스를 노출하고 이를 통해 목적에 부합하는 어플리케이션을 개발할 수 있도록 합니다. 따라서 본 프로젝트에서는 위의 4가지 핵심 컴포넌트를 먼저 개발하고 이것을 이용하면 얼마나 효율적이고, 적은 양의 코딩으로 고성능 서버 어플리케이션을 개발할수 있는지를 제시할 것입니다.


'프로젝트 > libSOPT' 카테고리의 다른 글

libSOPT다운로드  (0) 2010.03.24
libSOPT기반 echo server  (0) 2010.03.24
libSOPT::Server  (0) 2010.03.24
libSOPT::Executor  (0) 2010.03.24
libSOPT::Request_Handler  (0) 2010.03.24
libSOPT::Stream_Handler  (0) 2010.03.24
libSOPT::Acceptor  (0) 2010.03.24
libSOPT 소개  (0) 2010.03.24
Posted by devop

댓글을 달아 주세요

프로그래밍/C/C++2010.03.23 16:16
1. Reactor 패턴
다중 이벤트를 처리하기 위한 전통적인 방법중 하나로 새로운 프로세스 또는 쓰레드를 생성하여 각각의 이벤트를 처리하는 방식이 있습니다. 이는 서버가 동시에 여러 네트워크 연결을 처리해야 할 경우에 즐겨 사용되는 방법입니다. 이러한 모델이 대부분의 상황에서 잘 동작하기는 하지만, 프로세스 또는 쓰레드의 생성 및 관리와 복잡성 증가라는 단점이 있습니다.

select(), poll(), WaitForMultipleObjects()와 같은 디멀티플렉서(demultiplexer)에 기초한 Reactor 모델은 단지 하나의 프로세스 또느 쓰레드에서 여러 개의 이벤트를  처리할 수 있도록 합니다.

어떻게 이와 같은 처리가 가능할까요?

Reactor패턴은 클라이언트가 접속하면 새로운 핸들을 생성하여 관리합니다. Reactor패턴은 다수의 핸들을 일괄적으로 관리하고 운영하는 과정에서 일차적으로 멀티플렉싱을 수행하고, 주기적으로 디멀티플렉싱을 통해 새롭게 발생한 이벤트가 존재하는지 확인합니다. 만약 새로운 이벤트가 존재한다면 하나씩 가져와 Dispatch를 진행합니다.


위 그림은 Reactor패턴의 구조를 보여주고 있습니다.

Reactor는 Event Handler를 실제로 구현한 구현 클래스(Concrete Event Handler)들을 등록, 해제, 호출을 위한 인터페이스를 가지고 있습니다. Event Handler는 Reactor 패턴이 필요로 하는 추상 인터페이스들을 가지고 있어 특정 어플리케이션에 비의존적인 매커니즘을 제공합니다. 또한 Event Handler는 input, output, timer events 등 여러가지 조건에 대해 반응할 수 있도록 인터페이스를 제공합니다.

이러한 Reactor와 Event Handler의 관계는 개발자의 비지니스 로직 부분을 이벤트 핸들링과 분리를 함으로써 어떠한 Event-driven 상황이 필요한 곳이라도  Reactor패턴을 적용시킬수 있게 합니다.

다음은 이러한 Reactor패턴을 이용한 echo server의 예제입니다.


2. echo server 예제
#include <ace/OS.h>
#include <ace/Log_Msg.h>
#include <ace/Message_Block.h>
#include <ace/INET_Addr.h>
#include <ace/Svc_Handler.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/SOCK_Stream.h>
#include <ace/Synch_Traits.h>
#include <ace/Reactor.h>
#include <ace/Acceptor.h>
#include <ace/Reactor_Notification_Strategy.h>

class Stream_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> {
private :
	typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> super;
	
	ACE_INET_Addr remote_addr_;
	ACE_Reactor_Notification_Strategy noti_;

public :
	Stream_Handler()
		: noti_(0, this, ACE_Event_Handler::WRITE_MASK)
	{ /* empty */ }

	//override
	virtual int open(void * = 0)
	{
		ACE_TRACE("Stream_Handler::open");
		if( super::open() == -1 )
			return -1;
		noti_.reactor(this->reactor());
		this->msg_queue()->notification_strategy(&noti_);
		if( this->peer().get_remote_addr(remote_addr_) == 0 )
		{
			ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### New client accepted: %s:%u\n", 
				remote_addr_.get_host_addr(), remote_addr_.get_port_number()));
		}
		return 0;
	}

	//override
	virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE)
	{
		ACE_TRACE("Stream_Handler::override");
		char buf[1024];
		ssize_t recv_cnt;
		if( (recv_cnt = this->peer().recv(buf, 1024)) <= 0 )
			return -1;
		ACE_Message_Block *mb;
		ACE_NEW_RETURN(mb, ACE_Message_Block(buf, recv_cnt), -1);
		mb->wr_ptr(recv_cnt);
		this->putq(mb);
		return 0;
	}

	//override
	virtual int handle_output(ACE_HANDLE handle = ACE_INVALID_HANDLE)
	{
		ACE_TRACE("Stream_Handler::handle_output");
		ACE_Message_Block *mb;
		ACE_Time_Value nowait(ACE_OS::gettimeofday());
		while( this->getq(mb, &nowait) != -1 )
		{
			ssize_t send_cnt = this->peer().send(mb->rd_ptr(), mb->length());
			if( send_cnt == -1 )
				ACE_ERROR((LM_ERROR, "[ERROR%T](%N:%l) ### %p\n", 
                                "Stream_Handler::handle_output"));
			else
				mb->rd_ptr(send_cnt);
			if( mb->length() > 0 )
			{
				this->ungetq(mb);
				break;
			}
			mb->release();
		}
		if( this->msg_queue()->is_empty() )
			this->reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK);
		else
			this->reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK);
		return 0;
	}
	
	//override
	virtual int handle_close(ACE_HANDLE handle, ACE_Reactor_Mask close_mask)
	{
		ACE_TRACE("Stream_Handler::handle_close");
		ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### Connection close %s:%u\n", 
				remote_addr_.get_host_addr(), remote_addr_.get_port_number()));
		return super::handle_close(handle, close_mask);
	}
	
};


int ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{
	ACE_INET_Addr listen;
	listen.set(9088);
	ACE_Acceptor<Stream_Handler, ACE_SOCK_ACCEPTOR> acceptor;
	acceptor.open(listen);
	ACE_Reactor::instance()->run_reactor_event_loop();
	ACE_RETURN(0);
}
Posted by devop
TAG ACE, c++, reactor

댓글을 달아 주세요

프로그래밍/C/C++2010.03.19 15:54
1. ACE
ACE에 대한 개요는 다음 게시물을 참조하기 바랍니다.


2. echo server 예제 
#include <ace/OS.h>
#include <ace/Log_Msg.h>
#include <ace/Message_Block.h>
#include <ace/INET_Addr.h>
#include <ace/Proactor.h>
#include <ace/Asynch_IO.h>
#include <ace/Asynch_Acceptor.h>

class Stream_Handler : public ACE_Service_Handler {
private :
	ACE_Asynch_Read_Stream	reader_;
	ACE_Asynch_Write_Stream writer_;
	ACE_INET_Addr			remote_address_;

public :
	~Stream_Handler()
	{
		if( this->handle() != ACE_INVALID_HANDLE )
			ACE_OS::closesocket(this->handle());
	}

	//override
	virtual void open(ACE_HANDLE new_handle, ACE_Message_Block &message)
	{
		ACE_TRACE("Stream_Handler::open");
		ACE_ASSERT(new_handle != ACE_INVALID_HANDLE);
		this->handle(new_handle);
		if( reader_.open(*this) != 0 || writer_.open(*this) != 0 )
		{
			ACE_ERROR((LM_ERROR, "[ERROR%T](%N:%l) ### %p\n", "Stream_Handler::open"));
			delete this;
			return;
		}
		ACE_Message_Block *mb;
		ACE_NEW_NORETURN(mb, ACE_Message_Block(1024));
		if( reader_.read(*mb, mb->space()) != 0 )
		{
			ACE_ERROR((LM_ERROR, "[ERROR%T](%N:%l) ### %p\n", "Stream_Handler::open"));
			delete this;
			return;
		}
		ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### New client accepted: %s:%u\n", 
				remote_address_.get_host_addr(), remote_address_.get_port_number()));
	}

	//override
	virtual void addresses(const ACE_INET_Addr &remote_address, const ACE_INET_Addr &local_address)
	{
		ACE_TRACE("Stream_Handler::addresses");
		ACE_UNUSED_ARG(local_address);
		remote_address_ = remote_address;
	}
	
	//override
	virtual void handle_read_stream(const ACE_Asynch_Read_Stream::Result &result)
	{
		ACE_TRACE("Stream_Handler::handle_read_stream");
		ACE_Message_Block &mb = result.message_block();
		if( !result.success() || result.bytes_transferred() == 0 )
		{
			ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### Connection close %s:%u\n", 
				remote_address_.get_host_addr(), remote_address_.get_port_number()));
			mb.release();
			delete this;
		}
		else
		{
			if( writer_.write (mb, mb.length ()) != 0 )
			{
				ACE_ERROR((LM_ERROR, "[ERROR%T](%N:%l) ### %p\n", 
                                       "Stream_Handler::handle_read_stream"));
				mb.release();
				delete this;
			}
			else
			{
				ACE_Message_Block *new_mb;
				ACE_NEW_NORETURN(new_mb, ACE_Message_Block(1024));
				reader_.read(*new_mb, new_mb->space());
			}
		}
	}
	
	//override
	virtual void handle_write_stream(const ACE_Asynch_Write_Stream::Result &result)
	{
		ACE_TRACE("Stream_Handler::handle_write_stream");
		result.message_block().release();
	}

};


int ACE_TMAIN(int agrc, ACE_TCHAR *argv[])
{
	ACE_INET_Addr listen;
	listen.set(9088);
	ACE_Asynch_Acceptor<Stream_Handler> acceptor;
	acceptor.open(listen, 
		0,	// byte to read
		1,	// call pass_address
		ACE_DEFAULT_ASYNCH_BACKLOG,
		1,	// reuse_addr
		0,	// default proactor
		0,	// validate new connection
		1,	// reissue accept
		-1);// number of initial accepts
	ACE_Proactor::instance()->proactor_run_event_loop();
	ACE_RETURN(0);
}

3. ACE와 Boost.Asio
ACE와 Boost.Asio는 멀티플랫폼 고성능 서버를 개발하기 위한 최고의 C++ 도구입니다.
(당분간 이 둘을 뛰어넘는 라이브러리는 나오기는 힘들것 이라고 개인적으로 생각합니다.)

특히 고성능 서버 개발을 위한 비동기 작업을 운영체제의 구분에 관계없이 Proactor라는 패턴을 통해 접근할 수 있습니다.
물론 두 라이브러리 모두 Proactor를 구현할수 없는 플랫폼에 대해서는 Reactor를 통해 Proactor를 시뮬레이션해주는 기능 또한 제공합니다.


ACE와 Boost.Asio를 프로젝트에 사용하게 됨으로써 얻는 효과는 분명하지만, 이 두 라이브러리의 진입 장벽이 결코 낮지 않습니다. C++은 손바닥 뒤집듯이 자유롭게 구사할 수 있어야 하며, 디자인 패턴, 운영체제, TCP/IP 및 네트워킹에 대한 이해를 필요로 합니다. 특히 ACE의 경우 Boost.Asio보다 더욱 방대한 영역을 다루고 있기 때문에 라이브러리가 더 크고 무거우며, 그만큼 더 많은 학습량을 요구합니다.

개인적으로는 ACE를 먼저 접하고 Boost.Asio를 접하게 되었는데, Boost.Asio를 이해하는데 많은 시간이 걸리지는 않았습니다. ACE와 Boost.Asio의 예제 코드를 확인해보면 서로 유사한 형태를 지니고 있음을 확인할 수 있는데, 이는 제가 의도적으로 코드를 비슷하게 작성한 것이 아니라, 두 라이브러리 모두 Proactor라는 디자인 패턴을 통해 비동기 오퍼레이션을 처리하기 때문입니다.

ACE는 Proactor를 비롯해서 Reactor, 쓰레드, 동기화 객체, 시그널 등 운영체제의 많은 부분을 캡슐화 하고 있습니다. Boost.Asio는 쓰레드나 동기화 객체와 같은 주제를 포함하지 않지만 Boost 라이브러리 전체로 범위를 넓혀보면 ACE와 마찬가지로 쓰레드, 동기화 객체, 시그널 등을 모두 포함하고 있음을 확인할 수 있습니다. 특히 Boost는 차세대 C++ 표준이라 불리우는만큼 성능이나 버그등에 대해서는 의심의 여지가 없으며, 기존의 코드와도 쉽게 결합할수 있습니다. 

따라서, ACE와 Boost.Asio 중 선택적으로 학습을 해야하는 경우라면 Boost.Asio를 먼저 추천하겠습니다.
Posted by devop

댓글을 달아 주세요