기술노트

ProudNet 없이 별도의 UDP 소켓으로 통신하기

권장하진 않지만 3rd party의 네트워크 엔진을 혼용하면서 ProudNet의 홀 펀칭 기능만 사용할 수 있는 기회를 제공하고 있습니다.

이를 통해 ProudNet이 클라이언트/서버 간 또는 P2P간 완성된 홀 펀칭 정보를 얻어 이를 토대로 ProudNet과 별개로 준비한 UDP소켓으로 통신할 수 있습니다.

ProudNet에서 얻은 홀 펀칭 정보는 Client가 가진 UDP소켓에 대해서 공유기가 매핑한 정보이기 때문에 이 UDP소켓을 꺼야 합니다. 이를 위해서 Proud.CNetClient.Disconnect를 호출하거나 Client 객체 파괴가 선결되어야 합니다. 그리고 나서 Client의 UDP소켓 주소를 3rd party에서 재사용 합니다.

이러한 과정없이 UDP소켓을 중복 생성한다면 ProudNet과 3rd party의 UDP소켓 모두 비정상적인 작동을 하게 될 것입니다.

주요 절차는 아래와 같습니다.

  1. 먼저, 홀 펀칭된 정보를 얻기 위해 Proud.CNetClient.GetDirectP2PInfo를 사용합니다.

  2. ProudNet이 점유한 UDP소켓을 해제합니다. Proud.CNetClient.InvalidateUdpSocketClient가 가진 UDP 소켓을 닫습니다. 이 때 공유기의 홀 펀칭 상태는 그대로 유지됩니다. Proud.CNetClient.InvalidateUdpSocket에서 리턴 된 값은 별도 모듈에서 피어 간 통신에 활용할 수 있습니다.

  3. Proud.CNetClient.InvalidateUdpSocket호출 후, Proud.CNetClient.RestoreUdpSocket을 쓰면 UDP 소켓을 새로 만들어 서버 및 피어 간의 UDP 통신을 재개합니다.

  4. 일부 공유기는 홀 펀칭이 되더라도 수십 초 ~ 수 분이 지나면 홀 펀칭 매핑이 임의로 변경되거나 증발할 수도 있습니다. ProudNet은 자체적인 relay fallback 기능으로 이 문제를 스스로 해결합니다. 하지만 3rd party UDP socket을 사용할 경우 이 문제는 개발자 또는 3rd party 엔진에서 직접 해결해야 하며 그렇지 않으면 P2P 통신이 실패할 수 있습니다.

C++ 함수C# 함수설명

Proud.CNetClient.GetDirectP2PInfo

Nettention.Proud.NetClient.GetDirectP2PInfo

다른 peer와 통신하기 위해 홀 펀칭된 정보를 가져옵니다.

Proud.CNetClient.InvalidateUdpSocket

Nettention.Proud.NativeNetClient.InvalidateUdpSocket

내부에 보유하고 있는 UDP 소켓을 종료한 후 서버와 P2P 통신을 강제로 바이패스 상태로 전환합니다.

Proud.CNetClient.RestoreUdpSocket

Nettention.Proud.NativeNetClient.RestoreUdpSocket

제거된 UDP 소켓을 다시 생성하고 상대 peer와의 UDP 홀 펀칭을 다시 시작합니다.

Proud.CNetClient.Disconnect

Nettention.Proud.NetClient.Disconnect

서버와 연결을 종료하고 모든 P2P 그룹에서 탈퇴합니다.

위와 같은 방법은 정상적인 사용법이 아닙니다. 따라서 use_alternative_p2p 에 의해 발생하는 문제에 대하여 ProudNet은 책임지지 않습니다.

DLL 프로젝트에서 ProudNet 사용하기

ProudNet을 static library로 사용하되 DLL 프로젝트에서 사용하려면 DllMain 함수의 process detach case에서 Proud.Thread.NotifyDllProcessDetached를 호출해야 합니다. 그렇지 않으면 프로그램 종료시 프리징 현상이 발생할 수 있습니다.

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    {
        Thread::NotifyDllProcessDetached();
    }
    break;
    }
    return TRUE;
}

웹 서버 기반 게임 서버에 ProudNet 붙이기

쉽게 게임 서버를 개발하기 위해 web application server (WAS)를 사용하지만 실시간 멀티플레이를 빠르게 처리하기 어렵거나 서버에서 게임플레이를 처리하기에 서버 부하가 너무 크다는 단점이 있습니다.

이를 보완하기 위해 ProudNet 같은 소켓 서버와 WAS를 혼용하여 사용하는 방법은 다음과 같습니다.

(1) 소켓 서버와 WAS 사이에 데이터베이스를 두어 서로 데이터를 공유하는 방법

직관적이고 단순하지만 데이터베이스에 처리량이 몰리고 데이터 공유 이상의 처리, WAS에서 소켓 서버에 요청을 보내고 응답받는 것이 불가능하다는 단점이 있습니다.

(2) 소켓 서버가 HTTP 요청 응답을 처리할 수 있는 기능을 탑재하고 WAS는 소켓 서버에 HTTP로 요청을 보내는 방법

이를 위해 Microsoft REST SDK 등을 소켓 서버에 쓰는 방법이 있습니다.

(3) WAS와 소켓 서버 사이에 HTTP가 아닌 소켓 통신을 하는 방법

WAS는 플러그인에 명령을 보내고 플러그인은 그 명령을 받아 소켓 서버로 보냅니다. ProudNet의 경우 NetClientLanClient를 사용하면 됩니다.

WAS의 플러그인 프로그래밍 방법

PHP: CGI or FastCGI ASP.NET: C# class library node.js: C plugin Java: JNI

나쁜 네트워크 환경 만들기

중도 UDP 통신을 막는 방법으로 나쁜 네트워크 환경을 흉내냅니다. Proud.CNetClient.TEST_FallbackUdpToTcp을 호출하여 UDP 통신을 일시적 또는 영구적으로 막을 수 있습니다.

TCP 지연 송신 기능과 Nagle 알고리즘

TCP는 기본적으로 지연 송신 기능을 내장하고 있는데, 이는 WAN 인터넷에서 통신 시 통신량을 효율적으로 유지할 수 있게 합니다. 그러나 송신해야 하는 데이터를 살짝 지연시켜서 상대방에게 송신합니다.

지연 송신 기능은 Nagle 알고리즘이라고 불리기도 합니다. Nagle 알고리즘은 0.01 ~ 0.7초 정도의 송신을 지연시키기 때문에 온라인 게임에서 부적합할 수 있기에 ProudNet에서는 이를 끄는 기능을 제공합니다. Nagle 알고리즘 끌 경우 Silly Window Syndrome을 방지하기 위해 최장 0.01초의 Delayed Send를 합니다.

Nagle 알고리즘을 제어하는 방법은 다음을 참고하십시오.

C++ 함수설명

Proud.CStartServerParameter.m_enableNagleAlgorithm

TCP 지연 전송 기능과 Nagle 알고리즘 기능을 켜거나 끕니다.

Proud.CStartLanServerParameter.m_enableNagleAlgorithm

-

Apple의 IPv6 정책 우회책

Apple의 정책으로 인해 서버 접속을 할 때 다음과 같은 형식의 주소를 사용할 수 없습니다.

11.22.33.44
1122:3344:5566:7788:1122:3344:5566:7788

그 대신 FQDN 형식의 호스트 이름을 써야 합니다.

myserver1.mygame.com

Apple의 정책을 빨리 대응해야 하지만 당장 모든 서버에 대한 FQDN 이름을 할당할 시간적 여유가 없다면, ProudNet에서 제공하는 우회적으로 해결하는 방법을 사용하실 수 있습니다.

접속하고자 하는 서버 주소 말고도, 다음과 같이 [1]과 [2]에 서로 다른 FQDN을 넣어주면 됩니다.

CNetClient* nc = CNetClient::Create();
p.m_serverIP = "11.22.33.44";
p.m_publicDomainName1 = "www.nettention.com"; // [1]
p.m_publicDomainName2 = "www.nts.go.kr";  // [2]
nc->Connect(p);

주의

  • [1]과 [2]는 서로 다른 도메인이어야 합니다.

  • [1]과 [2]는 IPv6 주소를 가지지 않아야 합니다.

  • NetClient는 [1]과 [2]의 서버에 실제로 접속하지는 않습니다. 하지만 [1]과 [2]는 유효한 호스트이어야 합니다.

본 우회책은 근본적인 해결 방법은 아니며 모든 NetServer에 대한 정상 접속을 보장하지 않습니다. 이 문제를 근본적으로 해결하기 위해서는 Apple 권고사항 대로 FQDN 형식의 호스트 이름을 써야 합니다.

유니코드-멀티바이트 상호 변환

ProudNet은 유니코드(Unicode)를 사용하기 때문에 개발 시 유니코드를 사용하는 것을 추천하지만, 유니코드를 쓰지 않는 프로그램이라도 ProudNet을 사용할 수 있습니다.

유니코드를 쓸 수 없는 경우, (기존에 제작한 프로그램이 이미 유니코드 기반이 아닌 경우 등) ProudNet에 입력되는 문자열을 유니코드로 변환하는 과정과 출력되는 문자열을 멀티바이트 코드 MBCS 로 변환해야 합니다.

이 역할을 하는 클래스는 Proud::StringA2W, Proud::StringW2A 입니다.

Proud::StringA2W 클래스는 MBCS를 유니코드로 변환하는 역할을 하고, Proud::StringW2A 는 유니코드를 반대로 MBCS로 변환합니다.

void Foo()
{
    // 유니코드에서 MBCS로 변환
    Proud::String a=L"가나다";
    const char* b = Proud::StringW2A(a);
    
    // MBCS에서 유니코드로 변환
    Proud::String c = Proud::StringA2W(b);
}

Last updated