P2P 통신 활용법

간단한 사용 방법

간단한 코드 추가로 P2P통신을 할 수 있습니다.

- PIDL 추가 예시

자세한 사용 예시는 PIDL 을 참고해 주세요.

//File Name C2C – Client간 
//Protocol의 정의파일 입니다.

// client-to-client RMI, 
// 최초메시지ID = 4000
global C2C 4000 
{
    P2PChat ([in] Proud::StringA txt);
}

- 서버

객체

Proud::CNetServer *srv 
         = Proud::CNetServer::Create();
Proud::HostID groupHostID;

P2P그룹 생성

// 주의! 실 Server에서는 
// GetClientHostIDs를 이용하지 말고, 
// 따로 관리하세요.
Proud::HostID list[100];
int listCount = 
        srv->GetClientHostIDs(
             list, 
             100);
  
groupHostID = 
        srv->CreateP2PGroup(
             list, 
             listCount, 
             ByteArray());

P2P그룹에 통신 보내기

g_S2CProxy.P2PChat(
         groupHostID, 
         Proud::RmiContext::ReliableSend,
         L"Hello~~~!");

P2P Group 파괴하기

srv->DestroyP2PGroup(groupHostID);

- 클라이언트

추가적으로 사용되는 부분 외에는 생략합니다.

Header

// Header File 추가
#include “../C2C_Stub.h”
// CPP File 추가
#include “../C2C_Stub.cpp”

Server & Client 구조에서 추가 되어야 할 부분

client ->AttachProxy(
         &g_C2CProxy);
client ->AttachStub(
         &g_C2CStub);

Event 객체 - 추가로 사용되는 부분

Class CClientEventSink : public INetClientEvent
{
       // 새로 추가되는 Group의 
       // member 수 만큼 연속으로 
       // 호출됩니다.
       virtual void OnP2PMemberJoin(
           HostID memberHostID, 
           HostID groupHostID, 
           int memberCount, 
           const ByteArray &customField) {}
  
       // 제거되는 Member 수 만큼 
       // 연속으로 호출됩니다.
       virtual void OnP2PMemberLeave(
           HostID memberHostID, 
           HostID groupHostID, 
           int memberCount) {}
  
       // 기타 생략
}

C2C 간 통신을 받기 위한 객체

Class C2CStub 
         : public C2C::Stub
{
DECRMI_C2C_P2PChat;
}
DEFRMI_C2C_P2PChat(C2CStub)
{
   Printf(
          “[client] %d, %s”, 
          remote, 
          txt);
}
C2CStub g_C2CStub;

C2C 간 통신을 보내기 위한 객체

C2C::Proxy g_C2CProxy;

Move Packet과 같이 자주 보내고 손실되어도 괜찮은 정보에는 Unreliable을 사용하는 것이 효율적입니다.

  • 홀 펀칭이 완료된 클라이언트 내부적으로 Reliable UDP를 사용하지만 재전송이 일어나므로 트래픽이 증가 할 수도 있습니다.

  • 홀 펀칭이 완료되지 않은 클라이언트 서버를 통해 Relay로 통신하는데 이때, Unreliable은 서버와 UDP로 통신하며, Reliable은 TCP로 통신합니다.

relay 통신할 때 P2P 메시징 총량 줄이기

Proud.CNetClient.GetPeerInfo 를 통해 RMI를 호출받을 타 클라이언트가 자신과 직접 P2P 통신을 하는 중인지 알 수 있습니다. 만약 relay 통신을 한다면 자신의 캐릭터 위치를 초당 8회씩 전송하는 RMI 호출 횟수를 초당 4회로 줄이는 방법이 있겠습니다.

JIT(Just-in-time) P2P 연결

ProudNet은 P2P 홀 펀칭을 필요 시에만 시행하도록 만들어져 있습니다.

가령, Proud.CNetServer.JoinP2PGroup() 을 이용해서 두 피어간 연결을 실제로 만들었다고 하더라도 두 피어 간 홀 펀칭을 실제로 하는 것은 사용자가 둘 간의 P2P 메시징을 시작할 때입니다.

이러한 기능은 NAT 라우터의 port mapping entry 과다 현상을 예방하는 효과를 가져다줍니다.

만약 두 피어 간 연결을 만드는 순간 바로 P2P 홀 펀칭을 원하신다면, Proud.CNetServer.SetDirectP2PStartCondition() 에서 JIT 대신 Always 값을 선택하시면 됩니다.

서버에서 다수의 클라이언트에게 routed multicast 하기

ProudNet에서는 P2P 기능을 활용해서 MMO 게임에서도 효과적인 멀티캐스트를 가능하게 합니다. server-to-client routed multicast는 P2P기능을 활용하여 클라이언트에서 서버의 통신을 Relay해주는 기능으로, 이를 활용하여 서버에서 발생되는 트레픽을 줄일 수 있습니다.

다음 조건들을 만족해야 server-to-client routed multicast 를 할 수 있습니다.

  • Unreliable 메시징 이어야 합니다.

  • 멀티캐스트 를 위해 1회 호출이어야 합니다. 여러 호스트에게의 송신을 위해 각 대상에 대해 RMI를 각각 호출하면 server-to-client routed multicast를 할 수 없습니다.

  • Proud.RmiContext 의 파라미터 멤버 Proud.RmiContext.m_unreliableS2CRoutedBroadcastMaxCount 를 채워 넣어야 합니다.

  • 필요하다면 Proud.RmiContext.m_unreliableS2CRoutedBroadcastMaxPing 도 채워 넣으시기 바랍니다. 디폴트: 기본값

  • 사전에 P2P간 통신을 한 적이 있거나 P2P 연결 조건을 JIT에서 always로 바꾸어야 합니다.

P2P 그룹 멤버로서 서버를 포함시키기

P2P 그룹에는 서버를 그룹 멤버로 추가할 수 있는데, 이는 P2P 그룹에게 보낼 메시지가 서버에게도 전송하고자 할 때 효과적입니다.

RMI stub이 클라이언트 뿐만 아니라 서버 측에도 연계되어 있어야 합니다.

가령, X라는 RMI 함수 그룹이 있을 경우, X::Proxy는 Proud.CNetClient.AttachProxy를 통해 연계되고, X::Stub은 Proud.CNetClient.AttachStub, Proud.CNetServer.AttachStub를 통해 연계되어야 서버도 P2P 그룹에게 보내는 RMI 호출을 받을 수 있습니다. 이후, Proud.CStartServerParameter.m_allowServerAsP2PGroupMember를 'true'로 설정하고 서버를 시작해야 서버도 P2P 그룹의 멤버로 들어가는 것이 허락됩니다.

P2P 그룹을 생성하거나 기존 P2P 그룹에 멤버를 추가할 때 파라미터로 Proud.HostID_Server 를 넣으면 서버도 P2P 그룹의 멤버로 들어갑니다.

Super Peer 중심의 P2P 네트워킹

소규모 멀티플레이 온라인 게임(MO 혹은 캐주얼) 게임에서 Peer 간 통신하는 방법 중 하나로, P2P 그룹의 멤버 중 하나가 게임 플레이를 위한 메시지 송수신을 모두 담당하는 방식이 있습니다. 이러한 방식을 Super Peer(수퍼피어 혹은 호스트) 중심의 P2P 네트워킹이라고 부르기도 합니다.

Super Peer 중심의 P2P 네트워킹 방식에서는, 게임을 플레이하는 동안 각각의peer는 보내야 할 메시지를 P2P 그룹 멤버 전원에게 직접 보내지 않습니다. 대신 지정된 1개의 Super Peer 에게 메시지를 보내고 이 메시지를 P2P 그룹에 있는 다른 모든 멤버들에게 직접 보냅니다.

P2P 그룹에 속한 클라이언트 중, 아주 좋은 인터넷 환경을 가진 클라이언트가 Super Peer 역할을 하면 효과적입니다. 이때 Super Peer 클라이언트에게 걸리는 트래픽은 다른 클라이언트들 갯수만큼의 수신량이 걸리며, 다른 클라이언트 갯수의 제곱만큼의 송신량이 걸리게 됩니다.

하지만 P2P 그룹에 속한 클라이언트 중 아무도 좋은 통신 속도를 갖추고 있지 않다면 게이밍 품질이 하락할 수 있습니다. 따라서 Super Peer 중심의 P2P 네트워킹 을 구현할 때는 Super Peer의 통신 품질을 고려하여 신중하게 선택해야 합니다.

- Super Peer 선택 방법

  • 인터넷 공유기가 아닌 직접 인터넷 회선에 물려 있는 peer

  • 송신 속도가 높은 peer

  • 성능이 좋아서 초당 실행 프레임 레이트가 높은 peer

- Super Peer의 실행 성능 및 통신량 고려 사항

Super Peer를 쓰는 목적 중 하나는 서버에서 감당하기 힘든 연산량을 클라이언트 중 하나가 차출하여 시행하는 데 있습니다. 물리 엔진 연산을 하고 그 연산 결과가 직접 영향을 주는 게임 플레이의 경우 Super Peer에서 처리를 대행하는 것이 좋습니다.

ProudNet은 Super Peer의 실행 성능을 판단하기 위해 frame rate를 씁니다. 이를 위해 다음과 같은 처리를 해야 합니다.

  • Super Peer 선정 과정에서 Proud.CSuperPeerSelectionPolicy.m_frameRateWeight 를 설정하거나 Proud.CSuperPeerSelectionPolicy.GetOrdinary() 를 사용합니다.

  • Proud.CNetClient.SetApplicationHint 를 통해 앱에서 측정된 frame rate를 Proud.CNetClient에 전달합니다.

가정용 인터넷의 초당 송신량은 통상적으로 30KB~200KB를 넘지 못하고 기복이 큽니다.

Super Peer에서는 다른 피어에 비해 초당 송신량이 월등히 높아야 하기 때문에 가정용 인터넷 하의 컴퓨터로 선정될 경우 통신량에 대한 조절이 필요하여 이를 위해 송신량 자동 조절 기능 (throttling) 을 참고하시기 바랍니다.


⬅️ 뒤로

Last updated