Version: 2017.1
호스트 이송(Host Migration)
Unity 멀티플레이어 설정(Setting up Unity Multiplayer)

전송 레이어 API 사용(Using the Transport Layer API)

Unity에서 제공하는 고수준 네트워킹 API는 플레이어, 네트워크 게임 오브젝트와 같은 흔한 요구 사항을 관리하는 사용하기 쉬운 시스템을 제공합니다. 이와 더불어 “전송 레이어”라는 저수준 API 역시 제공합니다. 이 API를 통해 저수준에서 스스로의 네트워킹 시스템을 빌드할 수 있으며, 이는 게임 네트워킹에 있어서 구체적이거나 고급 요구 사항이 있는 경우 유용합니다.

전송 레이어는 운영체제의 소켓 기반 네트워킹 위에서 동작하는 얇은 레이어입니다. 바이트 배열로 나타나는 메시지를 송수신할 수 있으며, 다양한 상황에 적응할 수 있도록 다양한 “서비스 품질” 옵션을 제공합니다. 레이어는 유연성과 성능에 초점을 두었으며, UnityEngine.Networking.NetworkTransport 클래스에서 API를 노출시킵니다.

전송 레이어는 네트워크 통신을 위한 베이스 서비스를 지원합니다. 서비스는 다음과 같습니다.

  • 연결 구축
  • 다양한 “서비스 품질(QoS)”을 활용한 통신
  • 흐름 제어
  • 베이스 통계
  • 릴레이 서버를 통한 통신 또는 로컬 디스커버리와 같은 추가 서비스

전송 레이어는 두 개의 프로토콜을 사용할 수 있습니다. UDP 프로토콜은 일반 통신에 사용되며, WebSockets는 WebGL에 사용됩니다. 전송 레이어를 직접 사용하는 일반적인 워크플로는 다음과 같습니다.

  1. 네트워크 전송 레이어 초기화
  2. 네트워크 토폴로지 설정
  3. 호스트 생성
  4. 통신 시작 (연결 관리 및 메시지 송수신)
  5. 라이브러리 사용 후 종료

네트워크 전송 레이어(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() 설정을 사용하는 것이 적절합니다.

설정(Configuration)

다음 단계는 피어 간 연결을 설정합니다. 다수의 통신 채널을 정의한 후, 송신하고자 하는 메시지의 타입에 적합한 서비스 품질을 각각 할당하고 게임에서 상대적인 우선순위를 지정하면 됩니다.

    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개의 연결을 지원하며, 각각의 연결은 이전 단계에서 정의한 파라미터에 의해 설정됩니다.

호스트 생성

모든 위의 사전 단계가 완료되었으면 호스트(오픈 소켓)를 생성할 수 있습니다.

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

위의 예제에서는 포트 8888에서 임의의 IP 주소를 가지는 호스트를 추가합니다. 이 호스트는 최대 10개의 연결을 지원하며, 각각의 연결은 config 오브젝트에서 정의한 파라미터를 포함합니다.

커뮤니케이션

호스트를 생성했으면 커뮤니케이션을 시작할 수 있습니다. 이를 위해서는 호스트에 몇몇 명령을 송신한 후 상태를 점검해야 합니다. 송신할 수 있는 주요 커맨드는 아래와 같이 3개가 있습니다.

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도 반환합니다. 두 번째 함수는 호스트 ID가 recHostId인지 확인합니다. 이들 함수는 모두 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 주소가 수신 주소인 경우, IP 주소로서 null을 전달할 수 있습니다. 이 경우 호스트는 모든 네트워크 인터페이스를 수신합니다. 서버는 단 하나의 Websocket Host만을 지원할 수 있으며, 동시에 일반 호스트도 처리할 수 있습니다.

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

위 예제는 웹 소켓 프로토콜을 처리하는 TCP 소켓을 8887 포트에 열고, 일반 프로토콜을 처리하는 UDP 소켓을 8888 포트에 엽니다.

호스트 이송(Host Migration)
Unity 멀티플레이어 설정(Setting up Unity Multiplayer)