Skip to content

Repository files navigation

Unity TCP Server/Client — Thread-Safe Example

Unity에서 TCP 통신을 구현할 때 발생하는 스레드 경계 문제와 메시지 프레이밍 문제를 함께 다룬 최소 예제입니다.

  • 워커 스레드 → 메인 스레드 전달: ConcurrentQueue 마샬링
  • 스트림 경계 문제: 4바이트 길이 프리픽스 프레이밍

왜 만들었나

Unity의 UI·Transform·GameObject API는 메인 스레드에서만 호출할 수 있습니다. 그러나 TCP 수신은 별도 워커 스레드에서 이루어지므로, 수신 콜백에서 UI를 직접 갱신하면 예외가 발생하거나 조용히 실패합니다.

실무에서 의료기기 관리 프로그램과 VR 클라이언트 간 TCP 통신을 설계하며 같은 문제를 다룬 경험이 있어, 그 구조를 회사 코드 없이 처음부터 다시 작성했습니다.

구조

[워커 스레드]                                     [메인 스레드]
AcceptLoopAsync  ─┐
ReceiveLoopAsync ─┼─→ ConcurrentQueue<Action> ─→ Update()에서 Dequeue
Log()            ─┘                                    └→ OnLog 이벤트 발행
                                                          └→ UI 갱신
  • 네트워크 계층은 UI를 참조하지 않고, Action을 큐에 넣습니다.
  • UI 계층은 스레드를 직접 참조하지 않고, 큐에서 Invoke되는 이벤트를 구독만 합니다.
  • 두 계층은 ConcurrentQueue 하나로만 연결됩니다.

메시지 프레이밍

TCP는 스트림 프로토콜이므로 송신 측의 Write 단위와 수신 측의 Read 단위가 온전히 일치하지 않습니다.

  • 뭉침: 연속 전송 시 OS가 여러 메시지를 묶어 전달
  • 잘림: 버퍼보다 큰 메시지가 여러 번에 나뉘어 도착 (UTF-8 한글은 3바이트이므로 경계에서 잘리면 복구 불가)

이를 해결하기 위해 본문 앞에 4바이트 길이 헤더를 붙이고, 수신 측에서 해당 길이만큼 정확히 채워질 때까지 읽는 구조를 적용했습니다.

┌──────────────┬──────────────────────┐
│ 길이 4바이트 │  본문 (UTF-8, 길이만큼) │
└──────────────┴──────────────────────┘

NetworkStream.ReadAsync는 요청한 바이트 수가 아니라 현재 도착한 바이트 수만 반환하므로, ReadExactAsync에서 루프를 돌며 버퍼를 채웁니다.

또한 손상되거나 악의적인 헤더가 과대 메모리 할당을 유발하지 않도록 본문 길이에 상한(64KB)을 두고 검증합니다.

검증

테스트 프레이밍 전 프레이밍 후
20개 연속 전송 여러 메시지가 한 줄로 뭉침 20줄로 정확히 분리
6000바이트 한글 전송 중간에 잘려 문자 깨짐 온전히 1줄로 수신

구현 내용

항목 방식
접속 수락 AcceptTcpClientAsync 비동기 루프
수신 클라이언트별 독립 ReceiveLoopAsync
스레드 경계 ConcurrentQueue<Action> + Update() 소비
종료 CancellationTokenSource로 전체 루프 일괄 취소
클라이언트 목록 lock 기반 동기화 + 브로드캐스트 시 스냅샷 복사
정리 OnDestroy / OnApplicationQuit에서 소켓 해제
메시지 프레이밍 4바이트 길이 프리픽스 + ReadExact 루프
방어 처리 본문 길이 상한(64KB) 검증으로 손상 헤더 대응

실행 방법

  1. Unity 6 이상에서 프로젝트 열기
  2. TCP_Sample 재생
  3. [서버 시작][접속] → 메시지 입력 후 [전송]
  4. 서버가 ECHO|메시지로 브로드캐스트하는 것을 로그에서 확인

알려진 한계

이 예제는 스레드 경계 처리와 메시지 프레이밍 시연이 목적이므로 다음은 다루지 않습니다.

  • 재연결 로직
  • 인증 및 암호화

환경

Unity 6000.0.68f1 / .NET Standard 2.1 / Windows 11

About

Unity TCP 예제 — ConcurrentQueue 메인 스레드 마샬링 + 4바이트 길이 프리픽스 메시지 프레이밍

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages