ACE 는 훌륭한 네트워크 프레임 워크 입니다. 서버를 작성할 때 신경써야 할 귀찮은 것들을 알아서 처리를 해줍니다. 그러한 ACE를 이용해서 우분투에서 간단한 에코서버(Echo Server)를 작성하는 방법을 알아보겠습니다. 

1. 설치 

그 귀찮은 ACE 받고 , 압축 풀고, 컴파일 하는 과정이 우분투에서는 간단한 명령 몇번이면 다 됩니다. 

sudo apt-get install libace-5.6.3 libace-dev libace-doc mpc-ace 

각각의 설명입니다. 
i   libace-5.6.3                    - C++ network programming framework         
i   libace-dev                      - C++ network programming framework develop
i   libace-doc                      - C++ network programming framework document
i   mpc-ace                         - makefile, project and workspace creator 

libace* 들은 ACE 관련 라이브러리와 헤더들입니다. mpc-ace 는 편하게 ACE 관련한 프로젝트 make 해주는 관리 툴입니다. (진짜 편합니다. 예전에 직접 작성해줄려고 하면 여러가지로 귀찮았습니다)


2. 환경 설정

.bashrc

export ACE_ROOT=/usr/share/ace
export LD_LIBRARY_PATH=/usr/lib:$ACE_ROOT/lib:$LD_LIBRARY_PATH 

라고 추가 설정해 주고 

source ~/.bashrc 

라고 입력해줍니다. 

3. 에코 서버 (Echo Server)

메아리 서버라고도 부르지요, 뭔가를 입력하면 똑같은 말을 돌려주는 기능을 가진 서버로 거의 모든 서버의 기본입니다. (서버 세계의 Hello World 라고나 할까요?)

이 소스는 어디선가 봐서 따온 것인데요. 누구것인지 기억이 안납니다. 혹시나 이 게시물을 보시게 되면 연락주세요 (__ 

9088 포트에 에코서버를 띄우고 클라이언트의 접속을 기다립니다. 

#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(¬i_);
		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);
}



또한 이 소스는 Reactor 를 쓰고 있는데 그것에 관한 설명은 다음으로 미루기로 하지요. 설명하자면 끝도 없을 테니까요. 위 내용을 server.cpp 로 저장합니다. 

4. mpc-ace 

mpc 는 복잡하게 작성해야만 했던 ACE 관련한 makefile 을 정말 편하게 만들어 주는 관리 툴입니다. 기본적으로 확장자가 mpc 인 파일이 필요합니다. 

server.mpc

project(server) : aceexe {
  exename = server
//  includes += directory_name other_directory
//  libpaths += /usr/X11R6/lib
 
  Header_Files {
    }
 
  Source_Files {
          server.cpp
  }
}

project(server) 를 보면 ( ) 안의 이름으로 make 파일이 만들어 집니다. 
aceexe 라고 쓰여진 것은 ace 관련된 실행파일로 만들라는 이야깁니다. 
exename = server 는 server 라는 이름의 실행파일이 만들어 집니다. 
includes 와 libpaths 는 추가되는 참조 디렉토리와 라이브러리를 더할 때 쓰입니다. 여기서는 안 쓰니 코멘트 처리합니다. 
Source_Files 에 컴파일할 대상을 적어줍니다. 현재 server.cpp 밖에 없습니다. 

mpc-ace server.mpc -type gnuace 

로 입력하면 GNUMakefile.server 라고 메이크파일이 하나 생성됩니다. 

make -f GNUMakefile.server 

로 입력하면 바로 컴파일 되고 실행파일이 생성됩니다. 





+ Recent posts