Unity Transport 2.5.1 Docs

API

https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/index.html

🔥Unity Transport?

Unity Transport 2.5.1

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/index.html

Unity Transport는 멀티플레이어 게임 개발을 위한 저수준 네트워킹 라이브러리입니다.

Unity의 두 가지 Netcode 솔루션인 Netcode for GameObjects(NFG)Netcode for Entities(NFE)의 기반으로 사용됩니다.

또한 사용자가 직접 만든 커스텀 네트워크 솔루션과 함께 사용할 수 있습니다.

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/index.html

Unity 엔진에서 지원하는 모든 플랫폼은 UDP 소켓 또는 WebSocket을 통해 제공되는 연결 기반 추상화 계층(내장 네트워크 드라이버) 덕분에 Unity Transport에서 원활하게 지원됩니다.

둘 다 암호화 유무에 따라 설정할 수 있으며, 위 블록 다이어그램에서 닫힌 자물쇠와 열린 자물쇠로 표시됩니다.

특히 파이프라인은 안정성, 패킷 순서 지정 및 패킷 조각화와 같은 추가적인 선택적 기능들을 제공합니다.

🍎 요구 사항

Unity Transport는 Unity 엔진이 지원하는 모든 플랫폼을 지원합니다.

WebGL의 경우, 클라이언트 모드에서 WebSocket 연결만 지원되며, Unity Relay를 사용하는 경우에는 호스팅도 가능합니다.

현재 문서는 Unity Transport 2.X 버전에 대한 설명이며, 이는 Unity Editor 2022.3 이상에서만 호환됩니다.

구 버전의 에디터는 Transport 1.X에서 지원됩니다. 아래는 호환성 표를 참고하세요.

Editor Version2021 LTS2022 LTS2023.1 and later
Transport 1.XYesYesNo
Transport 2.XNoYesYes

💡 참고사항

이 패키지는 Netcode for GameObjectsNetworkTransport 추상화 계층과 혼동하면 안됩니다.

더 자세한 내용은 해당  transports section of its documentation을 참고하세요.

🔥Sample Project

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/samples-usage.html

Unity Transport 패키지에는 Samples 폴더가 포함되어 있으며, 이 폴더에는 라이브러리의 기본적인 개념을 설명하는 간단한 어셈블리 정의와 관련된 씬들이 들어 있습니다.

이 샘플들은 패키지 매니저 창에서 Unity Transport 패키지를 선택했을 때 가져올 수 있습니다.

Unity Transport Sample
  • Cross-play Example : MultiNetworkDriver를 사용하여 UDP와 WebSocket 연결을 모두 허용하는 서버를 만드는 방법의 예입니다.
  • Jobified Client and Server : 작업을 사용하는 매우 간단한 클라이언트 및 서버 구현. 패키지 문서와 연계되도록 설계되었습니다.
  • Ping Sample : 클라이언트와 서버 간 왕복 시간(핑)을 계산하는 소규모 애플리케이션입니다.
  • Relay Sample(with Relay) : 유니티 릴레이를 사용하여 클라이언트와 호스트 간의 왕복 시간(핑)을 계산하는 작은 애플리케이션
  • SimpleClientServer : 가장 간단한 클라이언트 및 서버 구현. 패키지 문서와 연계되도록 설계되었습니다.

🔥간단한 클라이언트 서버

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/client-server-simple.html

이 예제는 Unity Transport 패키지의 모든 주요 기능을 다루며, 다음과 같은 API 사용법을 보여주는 샘플 프로젝트를 만드는 데 도움을 줍니다:

  • 전송 구성 (Configure the transport)
  • 연결 수립 (Establish a connection)
  • 데이터 전송 (Send data)
  • 데이터 수신 (Receive data)
  • 연결 종료 (Close a connection)
원격 “덧셈(add)” 기능을 구현

1️⃣ 서버 생성

서버는 들어오는 연결 요청을 듣고 메시지를 주고받는 엔드포인트를 생성합니다.

ServerBehaviour.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ServerBehaviour : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

2️⃣ 보일러플레이트 코드 (Boilerplate Code)

반복되는 작업이나 패턴에 대한 일종의 표준화된 코드인 Boilerplate를 생성합니다.

이 패키지는 완전한 제어권을 가지도록 선택한 아키텍처 설계로 저수준 API만 제공합니다.

따라서 약간의 Boilerplate 코드를 설정해야합니다.

1. NetworkDriver 선언 – Server

ServerBehaviour.cs

using UnityEngine;
using Unity.Collections;
using Unity.Networking.Transport;

public class ServerBehaviour : MonoBehaviour 
{
    NetworkDriver m_Driver;
    NativeList<NetworkConnection> m_Connections;
}
  • NetworkDriver: Unity Transport 패키지의 핵심 API로, 네트워크 통신을 제어합니다.
  • NativeList: 서버에 연결된 클라이언트들의 연결 정보를 저장하는 컨테이너 (Unmanaged Memory 사용).

2. 서버 초기화 (Start) – Server

void Start()
{
    m_Driver = NetworkDriver.Create();
    m_Connections = new NativeList<NetworkConnection>(16, Allocator.Persistent);

    var endpoint = NetworkEndpoint.AnyIpv4.WithPort(7777);
    if (m_Driver.Bind(endpoint) != 0)
    {
        Debug.LogError("Failed to bind to port 7777.");
        return;
    }
    m_Driver.Listen();
}
  1. 드라이버 생성 → 연결 리스트 초기화 (최대 16개 연결 수용)
  2. 모든 IPv4 주소(AnyIpv4)에서 7777 포트로 바인딩 시도
  3. 성공 시 Listen()으로 클라이언트 연결 대기 시작.

3. 리소스 정리 (OnDestroy) – Server

void OnDestroy() 
{
    if (m_Driver.IsCreated) {
        m_Driver.Dispose();
        m_Connections.Dispose();
    }
}
  • Unmanaged Memory를 직접 해제해야 메모리 누수 방지
  • IsCreated로 할당 여부 확인 후 해제

4. 서버 업데이트 루프 (Update) – Server

void Update() 
{
    m_Driver.ScheduleUpdate().Complete();

    // 1. 오래된 연결 정리
    for (int i = 0; i < m_Connections.Length; i++) {
        if (!m_Connections[i].IsCreated) {
            m_Connections.RemoveAtSwapBack(i);
            i--;
        }
    }

    // 2. 새 연결 수락
    NetworkConnection c;
    while ((c = m_Driver.Accept()) != default) {
        m_Connections.Add(c);
        Debug.Log("클라이언트 연결 수락");
    }

    // 3. 연결별 이벤트 처리
    for (int i = 0; i < m_Connections.Length; i++) {
        DataStreamReader stream;
        NetworkEvent.Type cmd;
        while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) 
               != NetworkEvent.Type.Empty) {
            
            if (cmd == NetworkEvent.Type.Data) {
                uint number = stream.ReadUInt();
                Debug.Log($"클라이언트로부터 {number} 수신, +2 처리");

                number += 2;
                m_Driver.BeginSend(NetworkPipeline.Null, m_Connections[i], out var writer);
                writer.WriteUInt(number);
                m_Driver.EndSend(writer);
            }
            else if (cmd == NetworkEvent.Type.Disconnect) {
                Debug.Log("클라이언트 연결 종료");
                m_Connections[i] = default;
            }
        }
    }
}
  • 데이터 수신: 클라이언트가 보낸 숫자를 읽어 +2 후 재전송.
  • 연결 종료Disconnect 이벤트 시 연결 리셋.

3️⃣ 클라이언트 구현 (차이점 중심) – Client

ClientBehaviour.cs

public class ClientBehaviour : MonoBehaviour {
    NetworkDriver m_Driver;
    NetworkConnection m_Connection; // 단일 연결만 관리

    void Start() {
        m_Driver = NetworkDriver.Create();
        m_Connection = m_Driver.Connect(NetworkEndpoint.LoopbackIpv4.WithPort(7777));
    }

    void Update() {
        m_Driver.ScheduleUpdate().Complete();

        if (!m_Connection.IsCreated) return;

        DataStreamReader stream;
        NetworkEvent.Type cmd;
        while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) 
               != NetworkEvent.Type.Empty) {
            
            if (cmd == NetworkEvent.Type.Connect) {
                Debug.Log("서버 연결 성공");
                uint value = 1;
                m_Driver.BeginSend(m_Connection, out var writer);
                writer.WriteUInt(value);
                m_Driver.EndSend(writer);
            }
            else if (cmd == NetworkEvent.Type.Data) {
                uint value = stream.ReadUInt();
                Debug.Log($"서버로부터 값 수신: {value}");
                m_Connection.Disconnect(m_Driver);
                m_Connection = default;
            }
            else if (cmd == NetworkEvent.Type.Disconnect) {
                Debug.Log("서버와 연결 종료");
                m_Connection = default;
            }
        }
    }
}

클라이언트 특이사항:

  • 단일 연결NetworkConnection 하나만 관리.
  • Connect 이벤트: 연결 성공 시 서버로 숫자 1 전송.
  • 데이터 처리: 서버 응답 후 즉시 연결 종료.

💡 중요:

모든 네트워크 작업은 비동기 Job 시스템 기반으로 동작하지만, 이 예제에서는 Complete()로 강제 동기화하여 간단히 구현했습니다.

실제 프로젝트에서는 Job 체인을 활용한 최적화가 필요합니다.

ServerBehaviour.cs

using Unity.Collections;
using Unity.Networking.Transport;
using UnityEngine;

namespace UnityNetworkingTransportSimpleTest
{
    public class ServerBehaviour : MonoBehaviour
    {

        NetworkDriver m_Driver;
        NativeList<NetworkConnection> m_Connections; // 연결된 클라이언트 목록

        private void Start()
        {
            m_Driver = NetworkDriver.Create(); // NetworkDriver 생성
            m_Connections = new NativeList<NetworkConnection>(16, Allocator.Persistent); // 최대 16개의 연결을 허용하는 NativeList 생성 (영구 할당)

            var endpoint = NetworkEndpoint.AnyIpv4.WithPort(7777); // 모든 IPv4 주소의 7777 포트에서 수신 대기
            if (m_Driver.Bind(endpoint) != 0)
            {
                Debug.LogError("포트 7777 바인딩 실패"); // 포트 바인딩 실패 시 에러 로그 출력
                return;
            }
            m_Driver.Listen(); // 들어오는 연결 요청 수신 시작
        }

        private void Update()
        {
            m_Driver.ScheduleUpdate().Complete(); // 네트워크 업데이트 스케줄링 및 완료

            // 1. 오래된 연결 정리
            for (int i = 0; i < m_Connections.Length; i++)
            {
                if (!m_Connections[i].IsCreated) { // 연결이 끊어진 클라이언트 정리
                    m_Connections.RemoveAtSwapBack(i); // 연결 리스트에서 제거
                    i--; // 인덱스 조정
                }
            }

            // 2. 새 연결 수락
            NetworkConnection newNetworkConnection;
            while ((newNetworkConnection = m_Driver.Accept()) != default)
            {
                m_Connections.Add(newNetworkConnection); // 새로운 연결을 연결 리스트에 추가
                Debug.Log("클라이언트 연결 수락"); // 클라이언트 연결 수락 시 로그 출력
            }

            // 3. 연결별 이벤트 처리
            for (int i = 0; i < m_Connections.Length; i++)
            {
                DataStreamReader stream;
                NetworkEvent.Type cmd;
                while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != NetworkEvent.Type.Empty)
                {

                    if (cmd == NetworkEvent.Type.Data)
                    {
                        uint number = stream.ReadUInt(); // 수신된 데이터에서 unsigned integer 값 읽기
                        Debug.Log($"클라이언트로부터 {number} 수신, +2 처리"); // 클라이언트로부터 받은 값 로그 출력 및 처리 내용 설명

                        number += 2; // 받은 값에 2를 더함
                        m_Driver.BeginSend(NetworkPipeline.Null, m_Connections[i], out var writer); // 클라이언트에게 메시지 전송 시작 (신뢰성 없는 파이프라인 사용)
                        writer.WriteUInt(number); // 처리된 unsigned integer 값 쓰기
                        m_Driver.EndSend(writer); // 메시지 전송 완료
                    }
                    else if (cmd == NetworkEvent.Type.Disconnect)
                    {
                        Debug.Log("클라이언트 연결 종료"); // 클라이언트 연결이 종료되었을 때 로그 출력
                        m_Connections[i] = default; // 연결 상태 초기화
                    }
                }
            }
        }

        private void OnDestroy()
        {
            if (m_Driver.IsCreated)
            {
                m_Driver.Dispose(); // NetworkDriver 자원 해제
                m_Connections.Dispose(); // 연결 리스트 자원 해제
            }
        }
    }
}

ClientBehaviour.cs

using Unity.Collections;
using Unity.Networking.Transport;
using UnityEngine;

namespace UnityNetworkingTransportSimpleTest
{
    public class ClientBehaviour : MonoBehaviour
    {
        NetworkDriver m_Driver;
        NetworkConnection m_Connection; // 클라이언트는 단일 연결만 관리

        private void Start()
        {
            m_Driver = NetworkDriver.Create(); // NetworkDriver 생성
            m_Connection = m_Driver.Connect(NetworkEndpoint.LoopbackIpv4.WithPort(7777)); // 루프백 주소의 7777 포트로 서버에 연결 시도
        }

        private void Update()
        {
            m_Driver.ScheduleUpdate().Complete(); // 네트워크 업데이트 스케줄링 및 완료

            if (!m_Connection.IsCreated) return; // 연결이 생성되지 않았으면 더 이상 진행하지 않음

            DataStreamReader stream;
            NetworkEvent.Type cmd;
            while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != NetworkEvent.Type.Empty) { // 연결에서 발생한 네트워크 이벤트 처리

                if (cmd == NetworkEvent.Type.Connect)
                {
                    Debug.Log("서버 연결 성공"); // 서버 연결 성공 시 로그 출력
                    uint value = 1;
                    m_Driver.BeginSend(m_Connection, out var writer); // 메시지 전송 시작
                    writer.WriteUInt(value); // unsigned integer 값 쓰기
                    m_Driver.EndSend(writer); // 메시지 전송 완료
                }
                else if (cmd == NetworkEvent.Type.Data)
                {
                    uint value = stream.ReadUInt(); // 수신된 데이터에서 unsigned integer 값 읽기
                    Debug.Log($"서버로부터 값 수신: {value}"); // 서버로부터 받은 값 로그 출력
                    m_Connection.Disconnect(m_Driver); // 서버와 연결 종료
                    m_Connection = default; // 연결 상태 초기화
                }
                else if (cmd == NetworkEvent.Type.Disconnect)
                {
                    Debug.Log("서버와 연결 종료"); // 서버와 연결이 종료되었을 때 로그 출력
                    m_Connection = default; // 연결 상태 초기화
                }
            }
        }
    }
}

🔥사용 모범 사례

1️⃣ 파이프라인 사용 (Using pipelines)

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

파이프라인(Pipelines)은 Unity Transport의 핵심 기능으로, 기본 제공되는 비신뢰성 데이터그램(unreliable datagrams) 위에 선택적 기능 계층을 추가할 수 있게 해줍니다.

주요 적용 기능: 순서 보장(sequencing), 신뢰성(reliability), 패킷 분할(fragmentation) 등.

❓작동 원리 (How it Works)

파이프라인은 하나 이상의 스테이지로 구성된 순서로 정의됩니다.

메시지가 파이프라인을 통해 전송되면 스테이지를 순서대로 거치며, 첫 번째 스테이지의 출력이 두 번째 스테이지로 파이프됩니다.

따라서 첫 번째 스테이지에서 패킷에 헤더를 추가하면 두 번째 스테이지는 이 헤더를 포함한 전체 패킷을 처리합니다.

메시지가 수신되면 스테이지 체인을 역순으로 거칩니다.

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

예를 들어 FragmentationPipelineStage는 큰 메시지를 더 작은 조각으로 나누는 것을 가능하게 하고,

ReliableSequencedPipelineStage는 메시지를 순서와 전달을 보장하며 전송하는 것을 가능하게 합니다.

두 기능 모두를 제공하는 파이프라인을 만들고 싶다면 다음과 같이 만들 수 있습니다.

// In initialization code, before any connections are made.
var myPipeline = driver.CreatePipeline(typeof(FragmentationPipelineStage), typeof(ReliableSequencedPipelineStage));

이렇게 하면 메시지를 먼저 패킷에 맞게 더 작은 조각으로 분할한 다음, 각 조각이 신뢰성 있게 그리고 올바른 순서대로 전달되는 파이프라인이 생성됩니다.

이 과정은 아래 그림과 같이 설명됩니다. 조각에 있는 작은 주황색 부분은 순서 번호신뢰성 단계에서 추가된 기타 정보를 나타냅니다.

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

단계의 순서가 중요하다는 점에 유의하세요.

만약 단계를 거꾸로 구성했다면, 신뢰성 정보는 분할되지 않은 큰 메시지에만 추가되었을 것입니다.

이 경우, 조각 중 하나라도 손실되면 전체 메시지를 다시 전송해야 하므로 대역폭 효율이 떨어집니다.

하지만 신뢰성 단계(reliable stage)를 분할 단계(fragmentation) 이후에 배치하면, 조각 하나가 손실되더라도 그 조각만 다시 전송하면 되므로 더 효율적입니다.

새로운 파이프라인에서 메시지를 전송하려면

BeginSend를 사용할 수 있습니다:

driver.BeginSend(myPipeline, connection, out var writer);

메시지를 어느 파이프라인으로 받았는지 확인하려면

PopEvent 또는 PopEventForConnection의 마지막 인자를 사용하세요:

var eventType = driver.PopEvent(out _, out _, out var receivePipeline);
if (eventType == NetworkEvent.Type.Data)
{
    // 데이터 메시지는 receivePipeline 파이프라인에서 수신되었습니다.
}

🔔 주의

파이프라인은 서버와 클라이언트에서 항상 같은 방식으로 구성되어야 합니다.

즉, CreatePipeline 호출과 그 순서는 양쪽에서 동일해야 합니다.

Unity Transport는 이러한 불일치를 자동으로 막아주지 않으므로, 파이프라인 생성 코드는 서버와 클라이언트 간에 공유하는 것을 권장합니다.

✅ Fragmentation 파이프라인 단계

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

기본적으로 Unity Transport는 MTU(약 1400바이트) 안에 들어가는 크기의 메시지만 전송할 수 있습니다.

더 큰 메시지를 보내기 위해서는 이를 더 작은 조각으로 분할해야 하며, 이를 메시지 분할(Fragmentation) 이라고 합니다.

FragmentationPipelineStage가 포함된 파이프라인을 구성하면, 메시지를 자동으로 분할해 줍니다.

분할 전 최대 페이로드 크기는 NetworkDriver를 생성할 때 설정할 수 있습니다:

var settings = new NetworkSettings();
settings.WithFragmentationStageParameters(payloadCapacity: 10000);

var driver = NetworkDriver.Create(settings);
var fragmentedPipeline = driver.CreatePipeline(typeof(FragmentationPipelineStage));

설정 가능한 최대 값은 약 20MB이지만, 이 파이프라인 단계는 수 킬로바이트 크기의 페이로드에 최적화되어 있습니다.

기본값은 4096바이트이며, 이보다 훨씬 큰 메시지는 초기화 시점에 한 번 보내는 정도로만 사용하는 것을 권장합니다.

📌 참고:

여러 파이프라인 단계를 조합할 경우, FragmentationPipelineStage일반적으로 가장 먼저 배치되어야 합니다.

대부분의 파이프라인 단계는 MTU보다 큰 패킷을 지원하지 않기 때문입니다.

✅ The reliable 파이프라인 단계

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

ReliableSequencedPipelineStage를 사용한 파이프라인은 TCP처럼 패킷의 순서 보장과 안정적인 전송을 보장합니다.

  • 패킷은 시퀀스 번호로 태그되며, 상대는 이 번호의 수신을 확인합니다.
  • 수신 확인이 되지 않으면 패킷은 재전송됩니다.
  • 순서가 어긋난 패킷은 버퍼에 저장되고, 이전 패킷이 도착할 때까지 대기합니다.

이 기능은 유용하지만, 멀티플레이어 게임에서는 과도하게 사용하면 성능에 악영향을 줄 수 있습니다.

신뢰성 있는 데이터 스트림은 헤드 오브 라인 블로킹(Head-of-line blocking) 으로 인해 지연이 발생할 수 있습니다.

따라서 정말 중요한 데이터 (예: RPC 호출, 캐릭터 동작 등)에만 이 단계를 사용하는 것을 권장합니다.

⛔ 동시 전송 가능한 패킷 수 제한

Reliable 단계는 동시에 전송 가능한 패킷 수가 제한됩니다.

기본값은 32개, 최대 64개까지 늘릴 수 있습니다.

이 제한은 연결별, 파이프라인별로 적용되며, 공유되지 않습니다.

팁: 가능한 한 신뢰성 있는 메시지를 배치(Batching) 하세요. 예: 20바이트짜리 메시지 2개를 따로 보내는 대신 40바이트로 묶어 한 번에 전송.

만약 이미 32개의 패킷이 전송 대기 중인 상황에서 또 보내려고 하면, EndSendNetworkSendQueueFull (값: -5)을 반환합니다.

이 경우 메시지를 큐에 저장하고 나중에 재전송하세요:

driver.BeginSend(myReliablePipeline, connection, out var writer);
// 메시지 작성
if (driver.EndSend(writer) == (int)Error.StatusCode.NetworkSendQueueFull)
{
    // 메시지를 큐에 저장 후 나중에 재전송
}
// 제한 늘리기

var settings = new NetworkSettings();
settings.WithReliableStageParameters(windowSize: 64);


var driver = NetworkDriver.Create(settings);
var reliablePipeline = driver.CreatePipeline(typeof(ReliableSequencedPipelineStage));

// 기본값은 32, 최대는 64입니다.
// 32보다 큰 값을 사용할 경우 헤더 크기가 약간 증가(4바이트) 하며, 실제 데이터 공간은 그만큼 줄어듭니다.

✅ The reliable 파이프라인 단계

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/pipelines-usage.html

SimulatorPipelineStage앱 테스트용으로 사용되며, 패킷 손실, 지연, 지터 등의 다양한 네트워크 조건을 시뮬레이션할 수 있습니다.

var settings = new NetworkSettings();
settings.WithSimulatorStageParameters(
    maxPacketCount: 100,
    mode: ApplyMode.AllPackets,
    packetDelayMs: 50);

var driver = NetworkDriver.Create(settings);
var simulatorPipeline = driver.CreatePipeline(typeof(SimulatorPipelineStage));


// maxPacketCount: 지연 가능한 최대 패킷 수. 이 수를 초과하면 지연 없이 전달됩니다.
// mode: 어떤 방향에 시뮬레이션을 적용할지 설정 (보내기, 받기, 또는 모두).
// packetDelayMs: 적용할 지연 시간 (ms). 일반 브로드밴드는 20ms, 열악한 모바일은 최대 200ms.
// packetJitterMs: 지연의 편차. 일반적으로 지연의 절반 이하가 적당합니다.
// packetDropPercentage: 손실할 패킷의 비율 (%). 나쁜 모바일 환경에서도 보통 3%를 넘지 않습니다.

📌 참고:

여러 단계로 구성된 파이프라인에서 SimulatorPipelineStage가장 마지막에 배치해야 합니다.

그렇지 않으면 다른 단계에서 처리되기 전에 패킷이 손실되어 의미가 없어질 수 있습니다 (예: Reliable 단계와 함께 사용할 경우).

2️⃣ Jobified 클라이언트 및 서버

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/client-server-jobs.html

Client 코드 (JobifiedClientBehaviour.cs)

using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Networking.Transport;

namespace Unity.Networking.Transport.Samples
{
    public class JobifiedClientBehaviour : MonoBehaviour
    {
        NetworkDriver m_Driver;
        NativeArray<NetworkConnection> m_Connection;

        JobHandle m_ClientJobHandle;

        void Start()
        {
            // 네트워크 드라이버 생성 및 초기화
            m_Driver = NetworkDriver.Create();
            m_Connection = new NativeArray<NetworkConnection>(1, Allocator.Persistent);

            // 로컬 호스트(127.0.0.1)의 포트 7777에 연결 시도
            var endpoint = NetworkEndpoint.LoopbackIpv4.WithPort(7777);
            m_Connection[0] = m_Driver.Connect(endpoint);
        }

        void OnDestroy()
        {
            // 이전 프레임의 잡이 모두 완료되도록 대기 후, 네이티브 리소스 해제
            m_ClientJobHandle.Complete();
            m_Driver.Dispose();
            m_Connection.Dispose();
        }

        void Update()
        {
            // 이전 프레임에서 스케줄된 작업 완료 대기
            m_ClientJobHandle.Complete();

            // 클라이언트 업데이트 작업 생성 및 설정
            var job = new ClientUpdateJob
            {
                Driver = m_Driver,
                Connection = m_Connection,
            };

            // 네트워크 드라이버 업데이트 스케줄링
            m_ClientJobHandle = m_Driver.ScheduleUpdate();

            // 클라이언트 작업 스케줄링 (드라이버 업데이트 이후 실행되도록 설정)
            m_ClientJobHandle = job.Schedule(m_ClientJobHandle);
        }

        // 클라이언트 상태를 업데이트하는 IJob 기반 작업
        struct ClientUpdateJob : IJob
        {
            public NetworkDriver Driver;
            public NativeArray<NetworkConnection> Connection;

            public void Execute()
            {
                // 연결이 유효하지 않으면 조기 종료
                if (!Connection[0].IsCreated)
                {
                    return;
                }

                DataStreamReader stream;
                NetworkEvent.Type cmd;

                // 이벤트 루프를 통해 네트워크 이벤트 처리
                while ((cmd = Connection[0].PopEvent(Driver, out stream)) != NetworkEvent.Type.Empty)
                {
                    if (cmd == NetworkEvent.Type.Connect)
                    {
                        Debug.Log("서버에 연결됨.");

                        // 서버에 uint 값 1을 전송
                        uint value = 1;
                        Driver.BeginSend(Connection[0], out var writer);
                        writer.WriteUInt(value);
                        Driver.EndSend(writer);
                    }
                    else if (cmd == NetworkEvent.Type.Data)
                    {
                        // 서버로부터 데이터를 수신하고 출력
                        uint value = stream.ReadUInt();
                        Debug.Log($"서버로부터 받은 값: {value}");

                        // 데이터 수신 후 연결 종료
                        Driver.Disconnect(Connection[0]);
                        Connection[0] = default;
                    }
                    else if (cmd == NetworkEvent.Type.Disconnect)
                    {
                        // 서버에서 연결이 종료되었음을 로그에 기록하고 상태 초기화
                        Debug.Log("서버와의 연결이 종료됨.");
                        Connection[0] = default;
                    }
                }
            }
        }
    }
}

Server 코드 (JobifiedServerBehaviour.cs)

using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Networking.Transport;

namespace Unity.Networking.Transport.Samples
{
    public class JobifiedServerBehaviour : MonoBehaviour
    {
        NetworkDriver m_Driver;
        NativeList<NetworkConnection> m_Connections;

        JobHandle m_ServerJobHandle;

        void Start()
        {
            // 네트워크 드라이버 생성 및 초기화
            m_Driver = NetworkDriver.Create();
            m_Connections = new NativeList<NetworkConnection>(16, Allocator.Persistent);

            // 포트 7777에 바인딩 시도
            var endpoint = NetworkEndpoint.AnyIpv4.WithPort(7777);
            if (m_Driver.Bind(endpoint) != 0)
            {
                Debug.LogError("포트 7777 바인딩 실패.");
                return;
            }

            // 클라이언트 연결 대기 시작
            m_Driver.Listen();
        }

        void OnDestroy()
        {
            if (m_Driver.IsCreated)
            {
                // 모든 잡 완료 후 리소스 해제
                m_ServerJobHandle.Complete();
                m_Driver.Dispose();
                m_Connections.Dispose();
            }
        }

        void Update()
        {
            // 이전 프레임의 잡 완료 대기
            m_ServerJobHandle.Complete();

            // 끊긴 연결 제거 및 새 연결 수락을 처리하는 잡 생성
            var connectionJob = new ServerUpdateConnectionsJob
            {
                Driver = m_Driver,
                Connections = m_Connections
            };

            // 클라이언트별로 데이터 수신 및 응답을 처리하는 병렬 잡 생성
            var serverUpdateJob = new ServerUpdateJob
            {
                Driver = m_Driver.ToConcurrent(),
                Connections = m_Connections.AsDeferredJobArray()
            };

            // 드라이버 업데이트 → 연결 처리 → 데이터 처리 순으로 잡 체인 스케줄링
            m_ServerJobHandle = m_Driver.ScheduleUpdate();
            m_ServerJobHandle = connectionJob.Schedule(m_ServerJobHandle);
            m_ServerJobHandle = serverUpdateJob.Schedule(m_Connections, 1, m_ServerJobHandle);
        }

        // 끊긴 연결을 정리하고 새 연결을 수락하는 작업
        struct ServerUpdateConnectionsJob : IJob
        {
            public NetworkDriver Driver;
            public NativeList<NetworkConnection> Connections;

            public void Execute()
            {
                // 유효하지 않은 연결 제거
                for (int i = 0; i < Connections.Length; i++)
                {
                    if (!Connections[i].IsCreated)
                    {
                        Connections.RemoveAtSwapBack(i);
                        i--;
                    }
                }

                // 새 클라이언트 연결 수락
                NetworkConnection c;
                while ((c = Driver.Accept()) != default)
                {
                    Connections.Add(c);
                    Debug.Log("클라이언트 연결 수락됨.");
                }
            }
        }

        // 각 연결로부터 데이터를 수신하고 처리하는 병렬 작업
        struct ServerUpdateJob : IJobParallelForDefer
        {
            public NetworkDriver.Concurrent Driver;
            public NativeArray<NetworkConnection> Connections;

            public void Execute(int i)
            {
                DataStreamReader stream;
                NetworkEvent.Type cmd;

                // 클라이언트 이벤트 루프
                while ((cmd = Driver.PopEventForConnection(Connections[i], out stream)) != NetworkEvent.Type.Empty)
                {
                    if (cmd == NetworkEvent.Type.Data)
                    {
                        // 숫자 수신 후 2를 더해 응답
                        uint number = stream.ReadUInt();
                        Debug.Log($"클라이언트로부터 {number} 수신. 2를 더해 전송.");

                        number += 2;

                        Driver.BeginSend(Connections[i], out var writer);
                        writer.WriteUInt(number);
                        Driver.EndSend(writer);
                    }
                    else if (cmd == NetworkEvent.Type.Disconnect)
                    {
                        // 연결 해제 처리
                        Debug.Log("클라이언트 연결 해제됨.");
                        Connections[i] = default;
                    }
                }
            }
        }
    }
}

3️⃣ 암호화된 통신 Encrypted communications

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/client-server-secure.html

Unity Transport 패키지는 서버/클라이언트의 신뢰성을 보장하는 동시에 서버와 클라이언트 간의 연결을 암호화하도록 구성할 수 있습니다.

4️⃣ Unity.Logging와 통합

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/logging.html

Unity Transport는 Unity Logging 패키지( )와의 선택적 통합을 제공합니다.

com.unity.logging package. 이 패키지는 기존 Unity 로깅 메커니즘에 대한 유연한 대안으로, 특히 프로덕션 서버에 유용합니다.

일반적으로 로그 메시지는 모두 를 거쳐 전송되지만 UnityEngine.Debug.Log, 로깅 패키지가 프로젝트에 포함되면 Unity Transport는 자동으로 Unity.Logging기본 로거 설정을 사용합니다.

특정 로그 설정을 조정하는 방법에 대한 자세한 내용은 로깅 패키지 설명서 사이트를 확인하세요.

🔥Cross-play support

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/cross-play.html

🔥WebGL support

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/websockets.html

🔥FAQ

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/faq.html

🔥Cross-play support

https://docs.unity3d.com/Packages/com.unity.transport@2.5/manual/migration.html

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤