ProudNet에서 RMI를 사용하는 방법은 아래 절차와 같습니다.
11.1PIDL 파일 작성하기
ProudNet에서 Remote Method Invocation(RMI)를 사용하려면 먼저 확장자가 PIDL인 파일을 만들어야 합니다.
PIDL 파일은 호스트간 주고받을 메시지를 정의하는 소스 파일입니다. PIDL 파일을 컴파일을 하면 proxy와 stub이 만들어지게 됩니다. 이들을 우리는 호스트 프로그램에 포함시킬 것입니다. proxy와 stub에 대한 자세한 설명은 4. Remote Method Invocation (원격 메서드 호출) 을 참고하시기 바랍니다.
PIDL 파일의 기본적인 사용 형식은 다음과 같습니다.
주의!! :
메시지의 첫 ID값으로 60000 이상의 값을 사용하지 마십시오. 6만 이상의 값은 ProudNet 내부 PIDL의 메시지 ID값으로 사용중입니다. 메시지 ID가 겹칠경우 AttachProxy 및 AttachStub 함수에서 exeption이 발생합니다.
global <네임스페이스> <메시지의 첫 ID 값> { ([특성 선언]) <함수 선언> ( [in] 함수 파라메터, ... ); }
여기서 각 부분에 대한 설명은 다음과 같습니다.
• 네임스페이스: PIDL 파일을 컴파일했을 때 생성되는 proxy, stub 모듈이 가지는 C++ namespace 이름입니다. ProudNet은 2개 이상의 proxy, stub 을 한꺼번에 사용할 수 있는데, 그 대신 namespace 로 모호성을 해결해야 하기 때문에 namespace 가 반드시 필요합니다. 예를 들어 LobbyC2S라고 선언하면, '게임 로비에서 클라이언트->서버 호출 RMI를 선언함'을 의미하겠죠.
• 메시지의 첫 ID 값: RMI가 호출되면 각 RMI는 네트웍 메시지로 변환됩니다. 이 네트웍 메시지는 ID 값을 메시지 헤더에 갖게 됩니다. 이 메시지 ID 의 처음 값을 의미합니다. 주의할 점은, 프로세스 간 통신을 위해 여러개의 proxy, stub 을 갖기 마련인데, 이들끼리 메시지 ID의 범위가 서로 겹치면 안됩니다. 자세한 사항은 16. RMI를 위한 메시지 범위
를 참고하십시오.
• 특성 선언: 아래 설명을 참고하세요.
• 함수 선언: RMI 함수의 이름입니다.
• 함수 파라메터: C++ 함수 선언과 비슷합니다. [in] 선언을 반드시 해줘야 합니다. 파라메터는 아래와 같은 선언이 허용됩니다. 단, 포인터 타입은 불허합니다.
int a std::vector<int> b CString c
함수 파라메터는 int 같은 언어 기본 타입 뿐만 아니라 개발자가 정의한 객체도 사용 가능합니다. 자세한 것은 marshaling을 참고하십시오.
PIDL 파일의 예는 아래와 같습니다.
global LobbyC2S 5000 // 메시지 ID가 5000부터 시작 { // ID=5001이 됩니다 Logon([in] CString name,[in] CString password); RequestRoomList([in] int pageNumber); // ID=5002가 됩니다 Chat([in] CString name); // ID=5003이 됩니다. }
참고
11.2PIDL 컴파일러 실행하기
리눅스에서 PIDL 컴파일러 실행하기
• Ubuntu에서 오픈소스 Mono 라이브러리 설치 예제 (※ 오픈소스 Mono 프로젝트는 리눅스, 윈도우, Mac OS 모두 지원함)
$sudo apt-get install mono-gmcs $sudo apt-get install mono-runtime
• CentOS에서 오픈소스 Mono 라이브러리 설치 예제 (※ 오픈소스 Mono 프로젝트는 리눅스, 윈도우, Mac OS 모두 지원함)
$sudo yum install mono-devel $sudo yum install mono-complete
• Mono 플랫폼에서 PIDL 컴파일러 실행 예제
$pwd /home/ProudNet/util $mono PIDL.exe ../Sample/Simple/Common/*.pidl -outdir ./
Windows에서 PIDL 컴파일러 실행하기
PIDL애드온을 이용하시면 편리하게 pidl 파일을 작성 및 컴파일할 수 있습니다.
PIDL애드온 이용방법은 다음 링크의 페이지에 나와있습니다.
http://guide.nettention.com/cpp_ko#pidl_addon
만약에 PIDL애드온을 이용하실 수 없는 상황이시라면 뒤이어 이어지는 내용인 PIDL Custom Build 설정 방법을 이용해주시면 됩니다.
• Visual Studio 프로젝트에서 PIDL Custom Build 설정
그림 11-1PIDL 파일 커스텀 빌드
빌드 설정의 각 세부 내용은 다음과 같습니다.
- 명령줄: <PIDL.exe가 있는 경로>\PIDL.exe "%(FullPath)" -outdir .\
- 설명: Compiling %(Filename).pidl ...
- 출력: %(RootDir)%(Directory)\%(Filename)_common.cpp;%(RootDir)%(Directory)\%(Filename)_common.h;%(RootDir)%(Directory)\%(Filename)_proxy.cpp;%(RootDir)%(Directory)\%(Filename)_proxy.h;%(RootDir)%(Directory)\%(Filename)_stub.cpp;%(RootDir)%(Directory)\%(Filename)_stub.h;%(Outputs)
- 추가 종속성: <PIDL.exe가 있는 경로>\PIDL.exe
이때 주의 사항이 있습니다.
• PIDL.exe 는 ProudNet의 util 폴더에 있습니다. (예: C:/Program Files/Nettention/ProudNet/util/pidl.exe)
• Debug, Release 빌드 모두 동일하게 설정을 해야 합니다!
설정이 모두 끝났으면 PIDL 파일을 컴파일해 보시기 바랍니다. 컴파일이 성공적으로 수행되면 아래와 같은 문구가 출력됩니다.
1>------ 빌드 시작: 프로젝트: ..., 구성: Release Win32 ------ 1>Compiling TestC2C.pidl ... 1>빌드 로그가 "..."에 저장되었습니다. 1>ProudNetTest_2005 - 오류: 0개, 경고: 0개 ========== 빌드: 성공 1, 실패 0, 최신 0, 생략 0 ==========
Visual Studio 2005, 2008 버전은 Custom Build Rules 라는 것을 지원합니다. 이를 활용하면 PIDL 파일을 추가할 때마다 매번 커스텀 빌드 설정을 해주지 않아도 자동으로 설정이 적용됩니다.
그림 11-2Custom Build Rules 설정 메뉴
Visaul Studio 2010 이후 버전의 경우 Build customizations 을 사용할 수 있습니다.
그림 11-3Custom Build Rules 설정 메뉴
Custom Build Rules 사용하기
Visual Studio 2005, 2008 에서는 Custom Build Rules 를 사용하여 Custom Build Rule 을 만들 수 있습니다.
Custom Build Rules 를 클릭하시면 나오는 UI 에서 "New Rule File..." 을 클릭하시면 룰 파일을 만들 수 있습니다.
Display Name : Custom Build Rules 에서 보여질 이름 입니다. ex) PIDL Rule
File Name : 룰 파일명을 작성합니다. ex) PIDL_Custom_Build_Rule
Directory : 룰 파일이 저장될 위치를 지정합니다. ex) C:\XXX\YYY
"Add Build Rule..." 을 클릭하시면 새로운 룰을 만들 수 있습니다.
그림 11-4Custom Build Rule 설정
설정의 각 세부 내용은 다음과 같습니다.
Additional Dependencies : ..\..\..\util\PIDL.exe Batching Separator : Command Line : ..\..\..\util\PIDL.exe "$(InputPath)" -outdir .\ Display Name : PIDL Execution Description : Compiling $(InputName).pidl ... File Extensions : *.pidl Name : PIDL Custom Build Rule Outputs : $(InputDir)\$(InputName)_common.cpp;$(InputDir)\$(InputName)_common.h;$(InputDir)\$(InputName)_proxy.cpp;$(InputDir)\$(InputName)_proxy.h;$(InputDir)\$(InputName)_stub.cpp;$(InputDir)\$(InputName)_stub.h
해당 설정을 마치시면 Custom Build Rules 에 다음과 같이 PIDL Build Rule 이 추가됩니다.
그림 11-5Custom Build Rules
새로 만들어진 룰 파일을 체크해 주시고 해당 프로젝트에서 파일을 만드시면 Build Tool 이 PIDL 로 자동으로 선택된 것을 확인하실 수 있습니다.
customizations 사용하기
Visual Stuido 2005, 2008 의 경우 Custom Build Rules 를 사용하여 룰 파일을 만들 수 있습니다.
Visual Studio 2010 이후 버전의 경우 룰 파일을 사용은 할수 있지만 룰 파일을 작성하는 UI 가 제거되어 작성을 할 수는 없습니다.
Visaul Studio 2005, 2008 에서 사용하는 룰 파일은 확장자가 .rules 인 1개의 파일 이지만 Visaul Studio 2010 이후 버전의 경우 .props, .targets, .xml 의 3개의 파일을 필요 하기 때문에 Visual Studio 2005, 2008 에서 만든 .rules 파일을 바로 사용 할 수 없습니다.
Visaul Studio 2010 이후 버전에서 customizations 을 사용을 하기 위해서 다음과 같은 2가지 방법 중 하나를 선택하실 수 있습니다.
• Visual Studio 2005 또는 2008 에서 프로젝트 생성 후 .rules 파일을 만들고 사용하도록 셋팅 후 해당 프로젝트를 2010 이후 버전으로 변환 시키는 방법.
• .props, .targets, .xml 파일을 생성하여 xml 코드를 직접 작성하여 사용하는 방법.
프로젝트를 변환 하실 경우 .rules 파일이 .props, .targets, .xml 로 자동으로 변환되어 생성됩니다.
Visual Studio 2005, 2008 과 Visual Studio 2010 이후버전은 정의된 메크로가 다릅니다. 해당 룰이 Visaul Studio 2010 이후 버전에서 사용되는 메크로로 작성되어 있지 않은 경우 변환을 하시기 전에 Visaul Studio 2010 이후 버전에서 사용하는 메크로로 변환을 해주시거나 변환 후 .props, .targets, .xml 를 각각 수정해 주셔야 합니다.
11.3RMI proxy와 stub을 연결하기
PIDL 컴파일 결과물의 클래스 이름
PIDL 파일에서 생성된 proxy, stub의 클래스 이름은 PIDL에서 지정한 네임스페이스의 Proxy, Stub라는 이름을 가진 클래스입니다. 가령, 아래 PIDL 파일
global TestC2S { ... };
에서 만들어진 proxy, stub은 각각 TestC2S.Proxy, TestC2S.Stub이 됩니다.
PIDL 컴파일 결과물의 파일 이름
먼저, PIDL 파일에서 생성된 소스 파일을 포함시켜야 합니다.
예를 들어, TestC2S.PIDL을 컴파일했다면 생성되는 Proxy 모듈 이름은 TestC2S_Proxy.cpp,TestC2S_Proxy.h입니다. Stub의 모듈 이름은 TestC2S_Stub.cpp, TestC2S_Stub.h가 됩니다.
Proxy나 Stub을 개발 중인 프로젝트에 직접 첨부하는 것은 좋지 않습니다. 왜냐하면 PIDL 파일의 컴파일 결과물은 비록 C++ 언어로 만들어져 있는 텍스트 파일이지만, 엄연히 컴파일 결과물이기 때문입니다. 예를 들어 Source control을 사용하고 있는 경우 컴파일 결과물이 read only 속성을 가진 파일이면 곤란하게 됩니다.
따라서 #include를 써서 포함시키는 것이 좋습니다.
클라이언트,서버에 Proxy 붙이기
PIDL 컴파일 결과물의 Proxy 인스턴스를 먼저 생성해야 합니다. 그리고 나서 인스턴스를 Proud.CNetClient 나 Proud.CNetServer에 등록해야 합니다.
클라이언트와 서버는 Proud.IRmiHost 를 상속받았으며, 여기에 있는 메서드 Proud.IRmiHost.AttachProxy 를 통해 proxy를 등록할 수 있습니다.
클라이언트나 서버 각각은 Proxy는 두개 이상을 붙여도 됩니다. 단, 메시지 ID의 범위가 겹쳐서는 안됩니다.
예를 들어, TestA.Proxy가 있고 TestB.Proxy를 한 개의 CNetClient에 붙인다고 가정했을때, 만약 TestA의 첫 메시지 ID를 2100으로 선언했고 TestB의 첫 메시지 ID를 2200으로 선언했다고 칩시다.
만약 TestA에 선언된 RMI 함수의 갯수가 200개인 경우, TestA에 배정되는 메시지 ID는 2100~2300이 됩니다. 이렇게 되면 TestB의 메시지 ID가 겹치게 됩니다.
만약 붙이는 Proxy의 메시지 ID가 겹치게 될 경우 예외가 throw됩니다.
클라이언트,서버에 Stub 붙이기
C++11을 사용하시면 아래와 같은 방법을 사용하셔도 좋습니다.
PIDL 컴파일 결과물의 Stub 인스턴스는 네트웍으로 수신된 메시지에 의해 실행될 RMI 함수들을 virtual function으로 갖고 있습니다. 개발자는 이 Stub 클래스를 상속받아서 RMI 함수들을 오버라이드해야 합니다.
PIDL컴파일러(ProudNet IDL)는 개발자의 편의를 위해, 다음과 같은 형태의 매크로를 생성합니다. 이 매크로는 RMI 함수 이름과 파라메터가 포장된 것들입니다.
#define DECRMI_S2C_ShowChat bool ShowChat(Proud::HostID remote,Proud::RmiContext &rmiContext,const CString &a,const int &b,const float &c) #define DEFRMI_S2C_ShowChat(DerivedClass) bool DerivedClass::ShowChat(Proud::HostID remote,Proud::RmiContext &rmiContext,const CString &a,const int &b,const float &c)
이들 매크로를 사용하는 절차는 다음과 같습니다.
상속된 Stub class의 class 선언에서 [DECRMI_네임스페이스_메서드] 를 추가합니다.
상속된 Stub class의 메서드 정의에서 [DEFRMI_네임스페이스_메서드] 를 사용합니다.
아래는 사용 예입니다.
// PIDL 파일 내용 global LobbyC2S 5000 { Foo([in] int a,[in] float b); } // LobbyC2S의 Stub을 상속받아 RMI를 구현하는 클래스 class LobbyC2SStub:public LobbyC2S::Stub { DECRMI_LobbyC2S_Foo; }; // LobbyC2S의 RMI Foo의 루틴 구현 DEFRMI_LobbyC2S_Foo(LobbyC2SStub) { // 여기에서 주어지는 파라메터는 PIDL 의 LobbyC2S.Foo 의 파라메터와 동일합니다. a++; b++; return true; }
자세한 예시는 Part III. ProudNet 튜토리얼의 RMI stub 구현하기를 참고하세요.
클라이언트와 서버는 Proud.IRmiHost를 상속받았으며, 여기에 있는 메서드 Proud.IRmiHost.AttachStub 을 통해 stub을 등록할 수 있습니다.
Proxy와 마찬가지로 메시지 ID가 겹치지 않는 조건하에 2개 이상의 Stub을 붙일 수 있습니다.
부착된 Proxy, Stub의 작동 방식
Proud.CNetServer나 Proud.CNetClient에 attach된 proxy, stub들은 Proud.CNetServer나 Proud.CNetClient 안에 있는 proxy list, stub list에 등록됩니다.
proxy 에서는 Proud.CNetServer 나 Proud.CNetClient 의 포인터를 갖고 있어서, Proud.CNetServer 나 Proud.CNetClient 의 송신 메서드를 호출합니다.
한편, 메시지를 Proud.CNetServer 나 Proud.CNetClient 에서 수신하면 그것을 stub list 에 던집니다. 각 stub은 받은 메시지의 헤더를 분석해서 적절한 RMI를 호출합니다. (만약 해당되는 RMI가 없으면 무시하고 다음 stub에게 분석 기회를 넘겨줍니다.)
Proud.CNetServer 나 Proud.CNetClient 가 파괴되면 그것들이 보유하고 있던 proxy list, stub list 는 연결 관계가 끊어집니다. 그러면 과거에 Proud.CNetServer 나 Proud.CNetClient 에 연결되어 있던 stub 들은 더 이상 RMI가 호출되지 않으며, proxy들도 더 이상 Proud.CNetServer 나 Proud.CNetClient 를 참조하지 않기 떄문에 RMI를 호출해도 아무런 작동을 하지 않습니다.
C++11에서의 Stub 붙이기
C++11의 람다식을 이용하면 람다 캡처를 통해 클래스 상속 관계를 생략하는 등 더 간결한 프로그래밍이 가능합니다. 아래와 같이 Stub을 구현할 때 람다식을 사용하세요.
1. StubFunctional 인스턴스를 생성하세요.
2. StubFunctional에서 구현하고자 하는 RMI 함수를 람다식 형식으로 정의하세요. 필요시 PARAM_ 매크로를 사용하세요.
아래는 람다식으로 RMI stub을 구현하는 예입니다.
<exam.pidl> Func1([in] int a, [in] string b); <exam.cpp> class Exam { int x = 1; Exam::StubFunction examStub; void Main() { int y = 3; examStub.Func1_Function = [this, y]PARAM_Exam_Func1 { x+=a; y+=a; return true; }; } };
11.4호스트간 통신하기
ProudNet에서 호스트간 서로 메시지를 주고 받기 위해 먼저 4. Remote Method Invocation (원격 메서드 호출)과 11. Remote Method Invocation 사용 방법
이 선결되어야 합니다. 여기서는 RMI 로 다른 호스트에 메시지를 전송하는 방법을 설명합니다.