using UnityEngine; using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; /// /// 중앙 UDP 수신 매니저. 하나의 포트에서 수신한 데이터를 여러 StreamingleFacialReceiver에 분배. /// 싱글톤으로 동작하며, Receiver가 Register 시 자동 생성됨. /// /// 사용법: /// StreamingleFacialReceiver에서 useSharedPort = true로 설정하면 /// 자동으로 이 마스터를 통해 UDP 데이터를 공유 수신합니다. /// 같은 포트에 여러 Receiver를 등록하면 동일한 데이터를 모든 Receiver가 받습니다. /// public class StreamingleFacialUdpMaster : MonoBehaviour { private static StreamingleFacialUdpMaster _instance; public static StreamingleFacialUdpMaster Instance { get { if (_instance == null) { var go = new GameObject("[StreamingleFacialUdpMaster]"); go.hideFlags = HideFlags.HideInHierarchy; DontDestroyOnLoad(go); _instance = go.AddComponent(); } return _instance; } } private class PortListener { public int port; public UdpClient udp; public Thread thread; public volatile bool isRunning; public volatile string latestMessage = ""; public string lastDistributedMessage = ""; public readonly HashSet receivers = new HashSet(); public readonly object lockObj = new object(); } private readonly Dictionary listeners = new Dictionary(); /// /// Receiver를 지정 포트에 등록. 해당 포트의 리스너가 없으면 자동 생성. /// public void Register(int port, StreamingleFacialReceiver receiver) { if (!listeners.TryGetValue(port, out var listener)) { listener = new PortListener { port = port }; listeners[port] = listener; StartListener(listener); } lock (listener.lockObj) { listener.receivers.Add(receiver); } Debug.Log($"[FacialUdpMaster] Port {port} 등록 (총 {listener.receivers.Count}개 Receiver)"); } /// /// Receiver를 지정 포트에서 해제. 해당 포트에 Receiver가 없으면 리스너 종료. /// public void Unregister(int port, StreamingleFacialReceiver receiver) { if (!listeners.TryGetValue(port, out var listener)) return; bool shouldStop = false; lock (listener.lockObj) { listener.receivers.Remove(receiver); shouldStop = listener.receivers.Count == 0; } if (shouldStop) { StopListener(listener); listeners.Remove(port); Debug.Log($"[FacialUdpMaster] Port {port} 리스너 종료 (Receiver 없음)"); } } /// /// 포트 변경 시 호출. 기존 포트 해제 → 새 포트 등록. /// public void SwitchPort(int oldPort, int newPort, StreamingleFacialReceiver receiver) { Unregister(oldPort, receiver); Register(newPort, receiver); } /// /// 특정 포트의 리스너를 재시작 (포트 문제 시 복구용) /// public void RestartPort(int port) { if (!listeners.TryGetValue(port, out var listener)) return; StopListener(listener); // 리스너 상태 리셋 후 재시작 listener.latestMessage = ""; listener.lastDistributedMessage = ""; StartListener(listener); Debug.Log($"[FacialUdpMaster] Port {port} 리스너 재시작"); } /// /// 현재 등록된 포트와 각 포트의 Receiver 수를 반환 (디버그용) /// public Dictionary GetPortStatus() { var status = new Dictionary(); foreach (var kvp in listeners) { status[kvp.Key] = kvp.Value.receivers.Count; } return status; } private void StartListener(PortListener listener) { try { listener.udp = new UdpClient(listener.port); listener.udp.Client.ReceiveTimeout = 5; listener.isRunning = true; listener.thread = new Thread(() => ListenThread(listener)) { IsBackground = true }; listener.thread.Start(); Debug.Log($"[FacialUdpMaster] Port {listener.port} 수신 시작"); } catch (Exception e) { Debug.LogError($"[FacialUdpMaster] Port {listener.port} 시작 실패: {e.Message}"); } } private void StopListener(PortListener listener) { listener.isRunning = false; try { if (listener.udp != null) { listener.udp.Close(); listener.udp.Dispose(); listener.udp = null; } } catch (Exception) { } if (listener.thread != null && listener.thread.IsAlive) { listener.thread.Join(300); listener.thread = null; } } private void ListenThread(PortListener listener) { while (listener.isRunning) { try { IPEndPoint remoteEP = null; byte[] data = listener.udp.Receive(ref remoteEP); if (data != null && data.Length > 0) { listener.latestMessage = Encoding.ASCII.GetString(data); } } catch (SocketException e) { if (!listener.isRunning) break; if (e.SocketErrorCode != SocketError.TimedOut) { Debug.LogError($"[FacialUdpMaster] Port {listener.port} 수신 오류: {e.Message}"); } } catch (ObjectDisposedException) { break; } catch (Exception e) { if (!listener.isRunning) break; Debug.LogError($"[FacialUdpMaster] Port {listener.port} 오류: {e.Message}"); } Thread.Sleep(5); } } void Update() { foreach (var kvp in listeners) { var listener = kvp.Value; string msg = listener.latestMessage; if (string.IsNullOrEmpty(msg) || msg == listener.lastDistributedMessage) continue; listener.lastDistributedMessage = msg; lock (listener.lockObj) { foreach (var receiver in listener.receivers) { if (receiver != null && receiver.isActiveAndEnabled) { receiver.SetMessageFromMaster(msg); } } } } } void OnDestroy() { foreach (var kvp in listeners) { StopListener(kvp.Value); } listeners.Clear(); if (_instance == this) _instance = null; } void OnApplicationQuit() { OnDestroy(); } }