An article about Unity acting as both client and server
Communication protocols TCP, UDP
The server has many repeated methods, which is interesting You can write it as a base class to reduce the repetition rate
I. Introduction
Realize sending specified commands through TCP or UDP protocols, and the receiving end performs operations related to activation of control objects;
Pay attention to the summary at the end;
Demo Download
2. TCP protocol
Client (sender)
public class TCPSender : MonoBehaviour
{
[Header("目标IP地址")] public string ipAddress = "127.0.0.1";
[Header("目标端口号")] public int port = 12345;
[Header("发送内容")] public string hexCode = "FE 04 00 00 00 01 25 C5";
[Header("发送按钮")] public Button Btn_Send;
private TcpClient client;
private NetworkStream stream;
private void Start()
{
Btn_Send.onClick.AddListener(OnButtonClick);
client = new TcpClient(ipAddress, port);
}
public void OnButtonClick()
{
stream = client.GetStream();
byte[] data = Encoding.UTF8.GetBytes(hexCode);
stream.Write(data, 0, data.Length);
}
private void OnApplicationQuit()
{
client.Close();
}
}
Server (receiving end)
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public class TCPSever : MonoBehaviour
{
private TcpListener tcpListener;
[Header("接收内容")] public string Response;
[Header("是否激活对象")] public bool IsActive;
[Header("待激活对象")] public GameObject[] OBJ;
[Header("服务器端口")] public int Port = 12345;
[Header("是否需要激活时间")] public bool IsActiveTime;
[Header("激活时间")] public float ActiveTime;
private float ActiveTimeTemp;
private bool isStart;
private UnityMainThreadDispatcher mainThreadDispatcher;
private void Start()
{
try
{
// 创建TCP监听器
tcpListener = new TcpListener(IPAddress.Any, Port);
tcpListener.Start();
Debug.Log("TCP服务器已启动,等待客户端连接...");
//开启主线程
mainThreadDispatcher = UnityMainThreadDispatcher.Instance;
// 在新线程中等待客户端连接
System.Threading.Thread acceptThread = new System.Threading.Thread(AcceptClients);
acceptThread.Start();
}
catch (Exception e)
{
Debug.LogError("发生错误: " + e.Message);
}
}
private void AcceptClients()
{
while (true)
{
// 等待客户端连接
TcpClient client = tcpListener.AcceptTcpClient();
Debug.Log("客户端已连接: " + ((IPEndPoint)client.Client.RemoteEndPoint).Address);
// 在新线程中处理客户端请求
System.Threading.Thread clientThread = new System.Threading.Thread(() => HandleClient(client));
clientThread.Start();
}
}
private void HandleClient(TcpClient client)
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
{
// 处理客户端发送的数据
string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Debug.Log("接收到消息: " + data);
//处理消息
Client(data);
// 可以在这里回复客户端
string response = "服务器已接收消息: " + data;
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
}
client.Close();
}
catch (Exception e)
{
Debug.LogError("处理客户端请求时发生错误: " + e.Message);
}
}
private void OnApplicationQuit()
{
if (tcpListener != null)
{
tcpListener.Stop();
}
}
public void Client(string received)
{
if (received != null)
{
//表现
Response = received;
ComparisonResponse(received);
}
}
//处理请求
public void ComparisonResponse(string response)
{
switch (response)
{
case "FE 04 00 00 00 01 25 C5":
TimeOnclick();
break;
}
}
public void TimeOnclick()
{
if (IsActiveTime)
{
isStart = true;
ActiveTimeTemp = ActiveTime;
}
else
{
ObjActive();
}
}
public void ObjActive()
{
mainThreadDispatcher.Enqueue(() =>
{
foreach (GameObject o in OBJ)
{
o.SetActive(IsActive);
}
});
}
private void Update()
{
if (isStart)
{
ActiveTimeTemp -= Time.deltaTime;
if (ActiveTimeTemp <= 0)
{
ObjActive();
isStart = false;
}
}
}
}
3. UDP protocol
Client (sender)
public class UdpSender : MonoBehaviour
{
[Header("目标IP地址")] public string ipAddress = "127.0.0.1";
[Header("目标端口号")] public int port = 12345;
[Header("发送内容")] public string hexCode = "FE 04 00 00 00 01 25 C5";
[Header("发送按钮")] public Button Btn_Send;
private UdpClient udpClient;
private void Start()
{
Btn_Send.onClick.AddListener(OnButtonClick);
}
public void OnButtonClick()
{
udpClient = new UdpClient(ipAddress, port);
byte[] data = Encoding.UTF8.GetBytes(hexCode);
udpClient.Send(data, data.Length);
}
private void OnApplicationQuit()
{
udpClient.Close();
}
}
Server (receiving end)
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
public class UDPSever : MonoBehaviour
{
private UdpClient udpServer;
[Header("接收内容")] public string Response;
[Header("是否激活对象")] public bool IsActive;
[Header("待激活对象")] public GameObject[] OBJ;
[Header("服务器端口")] public int Port = 12345;
[Header("是否需要激活时间")] public bool IsActiveTime;
[Header("激活时间")] public float ActiveTime;
private float ActiveTimeTemp;
private bool isStart;
private UnityMainThreadDispatcher mainThreadDispatcher;
private void Start()
{
try
{
// 启动UDP服务器
udpServer = new UdpClient(Port);
Debug.Log("UDP服务器已启动,等待客户端连接...");
mainThreadDispatcher = UnityMainThreadDispatcher.Instance;
// 在新线程中接收来自客户端的数据
IPEndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, Port);
StartServerReceiver(udpServer, clientEndPoint);
}
catch (Exception e)
{
Debug.LogError("发生错误: " + e.Message);
}
}
private void StartServerReceiver(UdpClient server, IPEndPoint clientEndPoint)
{
System.Threading.Thread serverThread = new System.Threading.Thread(() =>
{
while (true)
{
byte[] receivedData = server.Receive(ref clientEndPoint);
string receivedMessage = Encoding.UTF8.GetString(receivedData);
Client(receivedMessage);
// 在这里添加向客户端发送响应的代码
string responseMessage = "服务器已收到消息: " + receivedMessage;
byte[] responseData = Encoding.UTF8.GetBytes(responseMessage);
server.Send(responseData, responseData.Length, clientEndPoint);
}
});
serverThread.Start();
}
public void Client(string received)
{
if (received != null)
{
//表现
Response = received;
ComparisonResponse(received);
}
}
public void ComparisonResponse(string response)
{
switch (response)
{
case "FE 04 00 00 00 01 25 C5":
TimeOnclick();
break;
}
}
public void TimeOnclick()
{
if (IsActiveTime)
{
isStart = true;
ActiveTimeTemp = ActiveTime;
}
else
{
ObjActive();
}
}
public void ObjActive()
{
mainThreadDispatcher.Enqueue(() => {
foreach (GameObject o in OBJ)
{
o.SetActive(IsActive);
}
});
}
private void OnApplicationQuit()
{
udpServer.Close();
}
private void Update()
{
if (isStart)
{
ActiveTimeTemp -= Time.deltaTime;
if (ActiveTimeTemp <= 0)
{
ObjActive();
isStart = false;
}
}
}
}
4. Summary
For example, Ctrip, Invoke function, and controlling the visibility of objects must be operated on the main thread, and then when our server monitors the client time, multi-threading is enabled; if the main thread is not used, the operation of controlling the visibility of objects cannot be completed;
Solution: By creating the main thread, the following script can be instantiated and called directly without mounting.
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static readonly Queue<Action> executionQueue = new Queue<Action>();
private static UnityMainThreadDispatcher _instance = null;
public static bool Exists
{
get {
return _instance != null; }
}
public static UnityMainThreadDispatcher Instance
{
get
{
if (!Exists)
{
_instance = new GameObject("MainThreadDispatcher").AddComponent<UnityMainThreadDispatcher>();
}
return _instance;
}
}
void Update()
{
while (executionQueue.Count > 0)
{
executionQueue.Dequeue().Invoke();
}
}
public void Enqueue(IEnumerator action)
{
executionQueue.Enqueue(() => {
StartCoroutine(action); });
}
public void Enqueue(Action action)
{
executionQueue.Enqueue(action);
}
}