RMI 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.CNetClientProud.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 컴파일러 는 개발자의 편의를 위해, 다음과 같은 형태의 매크로를 생성합니다. 이 매크로는 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;
}

자세한 예시는 ProudNet 튜토리얼RMI stub 구현하기 를 참고하세요.

클라이언트와 서버는 Proud.IRmiHost를 상속받았으며, 여기에 있는 메서드 Proud.IRmiHost.AttachStub 을 통해 stub을 등록할 수 있습니다.

Proxy와 마찬가지로 메시지 ID가 겹치지 않는 조건하에 2개 이상의 Stub을 붙일 수 있습니다.

부착된 Proxy, Stub의 작동 방식

Proud.CNetServerProud.CNetClient 에 attach된 proxy, stub들은 Proud.CNetServerProud.CNetClient 안에 있는 proxy list, stub list에 등록됩니다.

proxy 에서는 Proud.CNetServerProud.CNetClient 의 포인터를 갖고 있어서, Proud.CNetServerProud.CNetClient 의 송신 메서드를 호출합니다.

한편, 메시지를 Proud.CNetServerProud.CNetClient 에서 수신하면 그것을 stub list 에 던집니다. 각 stub은 받은 메시지의 헤더를 분석해서 적절한 RMI를 호출합니다. (만약 해당되는 RMI가 없으면 무시하고 다음 stub에게 분석 기회를 넘겨줍니다.)

Proud.CNetServerProud.CNetClient 가 파괴되면 그것들이 보유하고 있던 proxy list, stub list 는 연결 관계가 끊어집니다. 그러면 과거에 Proud.CNetServerProud.CNetClient 에 연결되어 있던 stub 들은 더 이상 RMI가 호출되지 않으며, proxy들도 더 이상 Proud.CNetServerProud.CNetClient 를 참조하지 않기 떄문에 RMI를 호출해도 아무런 작동을 하지 않습니다.