Host Migration
인터넷 서비스

트랜스포트 레이어 API의 사용

Unity가 제공하는 고레벨의 네트워크 API는 플레이어와 네트워크 GameObject 그리고 다른 네트워크에 필요한 기능을 쉽게 관리할 수 있는 시스템을 갖추고 있지만, 이 외에도 “트랜스포트 레이어(Transport Layer)”이라는 저레벨 API도 사용 가능합니다. 이로 인해 저레벨에서 자신의 작업 시스템을 구축하는 것이 가능하게 되어 있습니다. 이것은 더 특별한, 또는 고급 요구사항이 게임 네트워크에 요구되는 경우에 유용합니다.

트랜스포트 레이어는 OS의 소켓-기반의 네트워킹에서 작동하는 얇은 계층입니다. 바이트 배열로 표시되는 메시지의 송수신 기능을 갖춘 다양한 유형에 대응하기 위해 “quality of service(서비스 품질)”를 변경할 수 있는 다양한 옵션을 제공합니다. 이는 유연성과 성능에 중점을 둔 것으로, UnityEngine.Networking.NetworkTransport 클래스의 API를 통해 사용할 수 있습니다.

트랜스포트 레이어는 네트워크 통신의 기본 서비스를 지원합니다. 여기에는 다음이 포함됩니다:

  • 커넥션 확립(Establishing Connections)
  • 다양한 “서비스 품질(quality of service)”을 사용한 통신
  • 흐름 제어(Flow control)
  • 기본 통계(Base statistics)
  • 릴레이 서버에 의한 통신이나 로컬 디스커버리와 같은 부가적인 서비스

트랜스포트 레어이는 두 개의 프로토콜을 사용할 수 있습니다: 전반적인 통신은 UDP, WebGL에는 WebSocket이 사용됩니다. 트랜스포트 레이어를 직접 사용하는 경우, 일반적인 워크 플로우는 다음과 같습니다:

1.\t네트워크 트랜스포트 레이어 초기화 3.\t네트워크 토폴로지 설정 4.\t호스트 생성 5.\t통신 (연결 및 메시지 송수신) 시작 6.\t사용 후, 라이브러리 셧 다운.

네트워크 전송 계층(Network Transport Layer) 초기화

트랜스포트 레이어를 초기화하는 경우, 기본 초기화(인수 없음)를 하거나 네트워크 레이어의 전반적인 동작을 제어하는 파라미터(예: 최대 패킷 크기와 스레드의 시간 제한 등)를 제공하는 형태 중 하나를 선택할 수 있습니다.

    // Initializing the Transport Layer with no arguments (default settings)
    NetworkTransport.Init();
    // An example of initializing the Transport Layer with custom settings
    GlobalConfig gConfig = new GlobalConfig();
    gConfig.MaxPacketSize = 500;
    NetworkTransport.Init(gConfig);

위의 두 번째 예에서, 트랜스포 레이어는 사용자가 “MaxPacketSize”값을 500으로 지정하면서 초기화합니다. 사용자 정의 Init()값은 특수한 네트워크 환경을 사용하고 해당 설정에 대해 명확하게 이해하고 있는 경우에만 사용하십시오. 기본적으로 인터넷을 통해 플레이하는 전형적인 멀티플레이어 게임 개발의 경우에는, 인수가 없는 기본 Init() 설정이 적절합니다.

설정

다음 단계는 피어(peer) 간의 연결 설정입니다. 때로는 여러 통신 채널을 정의해야 할 수 있습니다. 그 이유는, 어떤 메시지를 보내는 데에 적합한 서로 다른 서비스 품질이 요구될 경우가 있으며, 게임에서의 상대적인 중요성에 따라서도 나눠서 사용할 수 있기 때문입니다.

    ConnectionConfig config = new ConnectionConfig();
    int myReiliableChannelId  = config.AddChannel(QosType.Reliable);
    int myUnreliableChannelId = config.AddChannel(QosType.Unreliable);

위의 예에서는 서비스 품질 레벨 값이 서로 다른 두 통신 채널을 정의하고 있습니다. “QosType.Reliable”은 메시지를 전달하고, 그것이 확실히 보내지도록 합니다. “QosType.Unreliable”은 메시지가 보내진다는 보증은 하지 않지만, 빨리 전송됩니다.

또한, ConnectionConfig 오브젝트의 프로퍼티를 조정하여 각 커넥션마다 구성 설정을 할 수도 있습니다. 그러나 클라이언트에서 다른 클라이언트로 연결을 할 경우, 두 피어에서 설정을 동일하게 하지 않으면 오류 CRCMismatch에 의해 연결이 실패하게 됩니다.

토폴로지

네트워크 설정의 마지막 단계는 토폴로지 정의입니다. 네트워크 토폴로지는 접속 가능한 최대 회선 수를 정의하고 어떤 커넥션 설정을 사용할 지를 정의합니다:

HostTopology topology = new HostTopology(config, 10);

여기에서 생성한 토폴로지는 10회선까지 커넥션 가능하며, 그 각 커넥션이 앞서 정의한 파라미터에 의해 설정할 수 있도록 되어 있습니다.

호스트 생성(Host creating)

모든 사전 준비가 완료되면, 호스트(오픈 소켓)를 만들 수 있습니다:

int hostId = NetworkTransport.AddHost(topology, 8888);

여기에서, IP주소는 뭐든 상관없이 포트 8888에 새 호스트를 추가합니다. 이 호스트는 최대 10개의 연결을 지원하고, 각 연결이 config 오브젝트에 정의된 파라미터를 가집니다.

통신(Communication)

호스트가 만들어지면, 이제 통신을 시작할 수 있습니다. 통신을 하려면 각종 명령을 호스트에 보내고 그 상태를 확인합니다. 보낼 수 있는 기본 명령은 세 가지가 있습니다:

connectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkTransport.Disconnect(hostId, connectionId, out error);
NetworkTransport.Send(hostId, connectionId, myReiliableChannelId, buffer, bufferLength,  out error);
  1. 첫 번째 명령은 IP가 “192.16.7.21”이고 포트 8888인 피어에게 커넥트 요청을 보냅니다. 이 커넥션에 할당된 ID가 반환될 것입니다.
  2. 두 번째 명령은 커넥션 끊기 요청을 보냅니다
  3. 세 번째 명령은 connectionId와 동일한 ID를 가진 커넥션에 myReiliableChannelId와 동일한 ID를 가진 신뢰할 수 있는 채널을 사용하여 메시지를 보냅니다. 메시지는 buffer[]에 저장되며, 메시지의 길이는 bufferLength를 통해 정의됩니다.

호스트 상태를 확인하기 위해서, 다음 두 함수를 사용할 수 있습니다:

NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
NetworkTransport.ReceiveFromHost(recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

두 함수 모두 이벤트를 반환하는데, 첫 번째 함수는 어느 호스트로부터 이벤트인지 상관없이 모두 반환(또한 recHostId를 통해 호스트 ID 반환)하는 반면, 두 번째는 호스트가 recHostId의 ID를 가졌는지 확인합니다. 이 함수들은 모두 Update() 메소드 내에서 사용할 수 있습니다:

void Update()
{
    int recHostId; 
    int connectionId; 
    int channelId; 
    byte[] recBuffer = new byte[1024]; 
    int bufferSize = 1024;
    int dataSize;
    byte error;
    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
    switch (recData)
    {
        case NetworkEventType.Nothing:         //1
            break;
        case NetworkEventType.ConnectEvent:    //2
            break;
        case NetworkEventType.DataEvent:       //3
            break;
        case NetworkEventType.DisconnectEvent: //4
            break;
    }
}
  • 포인트 1: 어떠한 주목할만한 이벤트도 반환하지 않습니다.
  • 포인트 2: 커넥션 이벤트가 도착했습니다. 이것은 새로운 커넥션일 지도 모르고, 이전 커넥션 명령에 대한 응답일 수도 있습니다.
myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
    case NetworkEventType.ConnectEvent: 
        if(myConnectionId == connectionId)
            //my active connect request approved
        else
            //somebody else connect to me
        break;
    //...   
}
  • 포인트 3: 데이터를 수신했습니다. 이 경우에는, recHostId가 호스트를 정의하고, connectionId가 커넥션을 정의하며, channelId가 채널을 정의합니다. dataSize는 받은 데이터의 크기를 정의합니다. recBuffer가 데이터를 가지기에 충분한 크기이면 데이터가 버퍼에 복사됩니다. 그렇지 않은 경우, errorMessageToLong 에러가 포함되어 버퍼를 다시 할당하여 이 함수를 재호출해야 합니다.
  • 포인트 4: 커넥션 끊기 시그널이 도착했습니다. 이것은 확립된 커넥션이 끊어졌다는 신호일 수도 있고, 커넥션 요청이 실패했다는 신호일 수도 있습니다.
myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
    case NetworkEventType. DisconnectEvent: 
        if(myConnectionId == connectionId)
            //cannot connect by some reason see error
        else
            //one of the established connection has been disconnected
        break;
    \\...   
}

WebGL 지원

클라이언트에서 WebSocket을 지원하게 되었습니다. 클라이언트에서도 위의 단계(토폴로지 및 구성 포함)는 모두 동일할 것입니다. 웹 클라이언트는 스탠드얼론 플레이어인 서버에만 연결할 수 있습니다 (Win, Mac, Linux만 해당). 서버에서 다음을 호출하십시오.

NetworkTransport.AddWebsocketHost(topology, port, ip);

IP 주소가 수신 주소(listening address)인 경우, IP 주소에 null을 전달할 수 있습니다. 이 경우에서는 호스트는 모든 네트워크 인터페이스를 수신합니다. 서버가 대응할 수 있는 Websocket Host는 하나 뿐이며, 동시에 일반적인 호스트들을 처리할 수도 있습니다:

NetworkTransport.AddWebsocketHost(topology, 8887, null);
NetworkTransport.AddHost(topology, 8888);

웹 소켓 프로토콜을 처리하는 tcp 소켓을 8887 포트에서 열고, 일반적인 프로토콜을 처리하는 udp 소켓은 8888 포트에서 엽니다.

Host Migration
인터넷 서비스