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();
}
}