电脑端启动后自动广播自身存在,手机端启动后监听广播并发现服务器。
发现后自动建立 UDP 连接,双方可互发消息。
内置心跳检测,网络中断时会自动检测并提示断开
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections.Generic;
public class ComputerServer : MonoBehaviour
{
private const int broadcastPort = 1369;
private const int communicationPort = 1370; // 通信端口
private UdpClient broadcastClient;
private UdpClient commClient;
private Thread broadcastThread;
private Thread receiveThread;
private IPEndPoint clientEndpoint;
private bool isRunning = true;
private float lastHeartbeatTime;
private const float heartbeatInterval = 2f; // 心跳间隔
private const float timeoutThreshold = 5f; // 超时阈值
// 主线程任务队列
private Queue<System.Action> mainThreadActions = new Queue<System.Action>();
void Start()
{
// 初始化广播客户端
broadcastClient = new UdpClient();
broadcastThread = new Thread(BroadcastMessage);
broadcastThread.IsBackground = true;
broadcastThread.Start();
// 初始化通信客户端
commClient = new UdpClient(communicationPort);
receiveThread = new Thread(ReceiveMessages);
receiveThread.IsBackground = true;
receiveThread.Start();
// 主线程初始化时间
lastHeartbeatTime = Time.time;
}
// 将操作放入主线程队列
private void RunOnMainThread(System.Action action)
{
lock (mainThreadActions)
{
mainThreadActions.Enqueue(action);
}
}
void BroadcastMessage()
{
while (isRunning)
{
try
{
string message = "ServerHere";
byte[] data = Encoding.UTF8.GetBytes(message);
IPEndPoint broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
broadcastClient.Send(data, data.Length, broadcastEndpoint);
Debug.Log("Broadcast sent: ServerHere");
Thread.Sleep(1000);
}
catch (System.Exception e)
{
Debug.LogError("Broadcast error: " + e.Message);
}
}
}
void ReceiveMessages()
{
while (isRunning)
{
try
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
byte[] data = commClient.Receive(ref sender);
string message = Encoding.UTF8.GetString(data);
// 处理心跳包 - 使用主线程更新时间
if (message == "Heartbeat")
{
// 将时间更新操作放入主线程队列
RunOnMainThread(() => {
lastHeartbeatTime = Time.time;
});
Debug.Log("Received heartbeat from client");
SendMessageToClient("HeartbeatAck"); // 回复心跳确认
}
else
{
Debug.Log("Received from client: " + message);
// 处理客户端发送的业务消息
}
// 记录客户端端点(首次连接时)
if (clientEndpoint == null)
{
clientEndpoint = sender;
}
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.Interrupted)
{
Debug.Log("Receive thread interrupted");
}
else
{
Debug.LogError("Receive error: " + e.Message + " (Code: " + e.SocketErrorCode + ")");
}
}
catch (System.Exception e)
{
Debug.LogError("Receive error: " + e.Message);
}
}
}
// 发送消息到客户端
public void SendMessageToClient(string message)
{
if (clientEndpoint == null)
{
Debug.LogWarning("No client connected");
return;
}
try
{
byte[] data = Encoding.UTF8.GetBytes(message);
commClient.Send(data, data.Length, clientEndpoint);
}
catch (System.Exception e)
{
Debug.LogError("Send error: " + e.Message);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
SendMessageToClient("我是服务端");
// 处理主线程任务队列
while (mainThreadActions.Count > 0)
{
System.Action action;
lock (mainThreadActions)
{
action = mainThreadActions.Dequeue();
}
action.Invoke();
}
// 检测客户端超时
if (clientEndpoint != null && Time.time - lastHeartbeatTime > timeoutThreshold)
{
Debug.LogWarning("Client timeout, disconnect detected");
clientEndpoint = null; // 重置连接状态
}
// 定时发送心跳(如果已连接)
if (clientEndpoint != null && Time.time - lastHeartbeatTime > heartbeatInterval)
{
SendMessageToClient("Heartbeat");
lastHeartbeatTime = Time.time;
}
}
void OnDestroy()
{
isRunning = false;
// 安全终止线程
if (broadcastThread != null && broadcastThread.IsAlive)
{
broadcastThread.Interrupt();
}
if (receiveThread != null && receiveThread.IsAlive)
{
receiveThread.Interrupt();
}
// 关闭客户端
broadcastClient?.Close();
commClient?.Close();
}
}
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.UI;
using System.Collections.Generic;
//按下TAb发送消息
public class MobileClient : MonoBehaviour
{
private const int broadcastPort = 1369;
private const int communicationPort = 1370;
private UdpClient broadcastClient;
private UdpClient commClient;
private Thread broadcastThread;
private Thread receiveThread;
private string serverIP = null;
private IPEndPoint serverEndpoint;
private bool isRunning = true;
private float lastHeartbeatTime;
private const float heartbeatInterval = 2f;
private const float timeoutThreshold = 5f;
// 主线程任务队列
private Queue<System.Action> mainThreadActions = new Queue<System.Action>();
public Text statusText;
public InputField messageInput;
void Start()
{
try
{
broadcastClient = new UdpClient(broadcastPort);
broadcastClient.EnableBroadcast = true;
broadcastThread = new Thread(ReceiveBroadcast);
broadcastThread.IsBackground = true;
broadcastThread.Start();
// 在主线程初始化时间
lastHeartbeatTime = Time.time;
RunOnMainThread(() => {
statusText.text = "Searching for server...";
});
}
catch (System.Exception e)
{
Debug.LogError("Initialization error: " + e.Message);
RunOnMainThread(() => {
statusText.text = "Initialization failed";
});
}
}
private void RunOnMainThread(System.Action action)
{
lock (mainThreadActions)
{
mainThreadActions.Enqueue(action);
}
}
void ReceiveBroadcast()
{
while (isRunning && serverIP == null)
{
try
{
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = broadcastClient.Receive(ref anyIP);
string message = Encoding.UTF8.GetString(data);
if (message == "ServerHere")
{
lock (this)
{
if (IPAddress.TryParse(anyIP.Address.ToString(), out IPAddress parsedIp))
{
serverIP = parsedIp.ToString();
serverEndpoint = new IPEndPoint(parsedIp, communicationPort);
RunOnMainThread(() => {
statusText.text = "Found server: " + serverIP;
});
Debug.Log("Server IP found: " + serverIP);
InitializeCommunication();
}
else
{
Debug.LogError("Invalid server IP address");
}
}
}
}
catch (System.Exception e)
{
Debug.LogError("Broadcast receive error: " + e.Message);
}
}
}
void InitializeCommunication()
{
try
{
commClient = new UdpClient(new IPEndPoint(IPAddress.Any, communicationPort + 1));
receiveThread = new Thread(ReceiveServerMessages);
receiveThread.IsBackground = true;
receiveThread.Start();
RunOnMainThread(() => {
statusText.text = "Connected to server";
});
SendMessageToServer("Client connected");
}
catch (System.Exception e)
{
Debug.LogError("Communication initialization error: " + e.Message);
RunOnMainThread(() => {
statusText.text = "Failed to connect";
});
}
}
void ReceiveServerMessages()
{
while (isRunning && serverEndpoint != null)
{
try
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
byte[] data = commClient.Receive(ref sender);
if (sender.Address.ToString() == serverEndpoint.Address.ToString() &&
sender.Port == serverEndpoint.Port)
{
string message = Encoding.UTF8.GetString(data);
if (message == "Heartbeat")
{
// 将时间更新操作放到主线程
RunOnMainThread(() => {
lastHeartbeatTime = Time.time;
});
SendMessageToServer("HeartbeatAck");
Debug.Log("Received server heartbeat");
}
else if (message == "HeartbeatAck")
{
// 将时间更新操作放到主线程
RunOnMainThread(() => {
lastHeartbeatTime = Time.time;
});
Debug.Log("Received heartbeat ack");
}
else
{
Debug.Log("Received from server: " + message);
string msg = message;
RunOnMainThread(() => {
statusText.text = "Server: " + msg;
});
}
}
else
{
Debug.LogWarning("Received message from unknown source: " + sender.Address);
}
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.Interrupted)
{
Debug.Log("Receive thread interrupted");
}
else
{
Debug.LogError("Communication socket error: " + e.Message + " (Code: " + e.SocketErrorCode + ")");
}
}
catch (System.Exception e)
{
Debug.LogError("Communication receive error: " + e.Message);
}
}
}
public void SendMessageToServer(string message)
{
if (serverEndpoint == null || commClient == null)
{
Debug.LogWarning("No server connected");
return;
}
try
{
byte[] data = Encoding.UTF8.GetBytes(message);
commClient.Send(data, data.Length, serverEndpoint);
}
catch (System.Exception e)
{
Debug.LogError("Send to server error: " + e.Message);
}
}
public void OnSendButtonClick()
{
if (!string.IsNullOrEmpty(messageInput.text))
{
SendMessageToServer(messageInput.text);
messageInput.text = "";
}
}
void Update()
{
// 处理主线程队列
while (mainThreadActions.Count > 0)
{
System.Action action;
lock (mainThreadActions)
{
action = mainThreadActions.Dequeue();
}
action.Invoke();
}
if (serverIP != null)
{
// 检测服务器超时
if (Time.time - lastHeartbeatTime > timeoutThreshold)
{
RunOnMainThread(() => {
statusText.text = "Server disconnected";
});
Debug.LogWarning("Server timeout");
}
// 发送心跳
else if (Time.time - lastHeartbeatTime > heartbeatInterval)
{
SendMessageToServer("Heartbeat");
lastHeartbeatTime = Time.time;
}
}
if (Input.GetKeyDown(KeyCode.Tab))
SendMessageToServer("我是客户端");
}
void OnDestroy()
{
isRunning = false;
if (broadcastThread != null && broadcastThread.IsAlive)
{
broadcastThread.Interrupt();
}
if (receiveThread != null && receiveThread.IsAlive)
{
receiveThread.Interrupt();
}
broadcastClient?.Close();
commClient?.Close();
}
}