4.unity和服务器的通信
1213 字约 4 分钟
2024-12-19
本篇主要是讲unity和服务端的通信,其中会用到partial关键字和观察者设计模式,请自行查阅。
一、效果演示
二、Unity Udp通信
在Script/Server/Client
中创三个cs文件,分别为:
- UdpClient.Request.cs,用来处理发送请求
- UdpClient.Response.cs,用来处理接收的请求
- UdpClient.cs,用来管理生命周期、发送、接收请求等。
UdpClient.cs
public partial class UdpClient : MonoBehaviour
{
private static UdpClient instance; // 单例模式
public static UdpClient Instance { get { return instance; } }
private void Awake()
{
instance = this;
DontDestroyOnLoad(gameObject);
}
void Start()
{
InitUdpClient();
StartReceiving();
}
}
UdpClient.Requset.cs
public partial class UdpClient
{
private const int port = 12345;
// 谁在发送,发送请求的udp
private System.Net.Sockets.UdpClient udpClient = new System.Net.Sockets.UdpClient();
// 发送给谁,
private IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
private void InitUdpClient()
{
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); // 0:自己分配端口号 也可以指定具体的端口
}
}
UdpClient.Response.cs
public partial class UdpClient
{
// 开启协程,接收消息
private void StartReceiving()
{
// 使用协程接收数据
StartCoroutine(ReceiveData());
}
// 启动协程
private IEnumerator ReceiveData()
{
while (true)
{
yield return new WaitForSeconds(0.1f); // 限制接收频率,可以调整
if (udpClient.Available > 0)
{
ReceiveMessages(); // 处理接收的信息
}
}
}
private void ReceiveMessages()
{
IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, port);
try
{
byte[] receivedData = udpClient.Receive(ref listenEndPoint); // 收到消息
// 处理接收到的消息 TODD
}
catch (ObjectDisposedException)
{
Debug.Log("UdpClient已关闭。");
//break; // 可以选择停止接收
}
catch (Exception ex)
{
Debug.Log($"异常: {ex.Message}");
}
}
}
假设我们要输入用户名、密码进行登陆,那么我们可以考虑向服务器发送一个字符串: "用户名+空格+密码",然后服务器解析后判断是否匹配,再响应给客户端。注意,其中省略了观察者设计模式的构建。
UdpClient.cs
public partial class UdpClient : MonoBehaviour
{
private static UdpClient instance; // 单例模式
public static UdpClient Instance { get { return instance; } }
private void Awake()
{
instance = this;
RequestSubScribe(); // 开启request中的订阅
DontDestroyOnLoad(gameObject);
}
void Start()
{
InitUdpClient();
StartReceiving();
}
private void OnDestroy()
{
RequestUnSubScribe(); // 销毁request中的订阅
}
}
UdpClient.Requset.cs
public partial class UdpClient
{
// 谁在发送,发送请求的udp
private System.Net.Sockets.UdpClient udpClient = new System.Net.Sockets.UdpClient();
// 发送给谁,
private IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
public void RequestSubScribe()
{
Events.LoginRequest += Login; // 登陆
}
public void RequestUnSubScribe()
{
Events.LoginRequest -= Login;
}
private void InitUdpClient()
{
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); // 0:自己分配端口号 也可以指定具体的端口
}
private void Login(string userName, string password)
{
string str = userName + " " + password;
byte[] msg = Encoding.UTF8.GetBytes(str);
udpClient.Send(msg, msg.Length, serverEndPoint);
}
}
UdpClient.Response.cs
public partial class UdpClient
{
// 开启协程,接收消息
private void StartReceiving()
{
// 使用协程接收数据
StartCoroutine(ReceiveData());
}
// 启动协程
private IEnumerator ReceiveData()
{
while (true)
{
yield return new WaitForSeconds(0.1f); // 限制接收频率,可以调整
if (udpClient.Available > 0)
{
ReceiveMessages(); // 处理接收的信息
}
}
}
private void ReceiveMessages()
{
IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, port);
try
{
byte[] receivedData = udpClient.Receive(ref listenEndPoint); // 收到消息
Events.LoginResponse.Call(Encoding.UTF8.GetString(receivedData));
}
catch (ObjectDisposedException)
{
Debug.Log("UdpClient已关闭。");
//break; // 可以选择停止接收
}
catch (Exception ex)
{
Debug.Log($"异常: {ex.Message}");
}
}
}
Events.Request.cs
public partial class Events
{
public static Event<string, string> LoginRequest;// 登陆请求
}
Events.Response.cs
public partial class Events
{
public static Event<string> LoginResponse; // 登陆响应
}
在Script/Server
文件夹下再创建一个LoginServer.cs,用来处理登陆的逻辑。场景中的登陆按钮绑定ClickLogin
方法,然后获取用户名和密码的文本信息,再发送登陆请求。LoginResponse
方法订阅登陆响应事件(在UdpClient.Response.cs会调用Events.LoginResponse.Call
)
public class LoginServer : MonoBehaviour
{
public InputField userName;
public InputField password;
public Text message;
private void Awake()
{
SubScribe();
}
private void SubScribe()
{
Events.LoginResponse += LoginResponse; // 登陆请求
Events.LoginSuccess += OnLoginSuccess; // 登陆成功
}
private void UnSubScribe()
{
Events.LoginResponse -= LoginResponse;
Events.LoginSuccess -= OnLoginSuccess;
}
/// 获得登陆消息的响应信息
public void LoginResponse(string str)
{
message.text = str;
}
// 绑定的按钮事件
public void ClickLogin()
{
Events.LoginRequest.Call(userName.text, password.text); // 发送登陆请求
}
private void OnDestroy()
{
UnSubScribe();
}
}
三、服务端 Udp通信
前面都是unity中的操作,在服务端也是很简单的,只需要处理一下接收到的信息,再进行比较之后发送给客户端就可以了。
服务端中的UdpServer.cs
/// <summary>
/// 循环接收信息
/// </summary>
private static void ReceiveMessages()
{
// 接收所有ip发送过来的UDP消息
IPEndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
// 一直接收消息
while (true)
{
try
{
byte[] receivedData = udpServer.Receive(ref clientEndPoint);
Console.WriteLine(Encoding.UTF8.GetString(receivedData));
// 处理接收到的消息
HandleLoginMessage(receivedData, clientEndPoint);
}
catch (SocketException ex)
{
Console.WriteLine($"SocketException: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
}
}
private static void HandleLoginMessage(byte[] loginInfo, IPEndPoint client)
{
string[] strs = Encoding.UTF8.GetString(loginInfo).Split(' ');
string message = CheckLogin(strs[0], strs[1]) ? "登陆成功" : "用户名或密码错误";
byte[] msg = Encoding.UTF8.GetBytes(message);
udpServer.Send(msg, msg.Length, client);
}
private static bool CheckLogin(string userName, string password)
{
if ("张三".Equals(userName) && "123456".Equals(password)) return true;
else if ("里斯".Equals(userName) && "666666".Equals(password)) return true;
else if ("admin".Equals(userName) && "admin".Equals(password)) return true;
return false;
}