制作UDPNetMgr网络管理模块
这段代码定义了一个名为UDPNetMgr
的 Unity 脚本类,用于管理 UDP 网络通信,它作为单例存在,在Awake
方法中创建收发消息的线程,Update
方法处理接收到的消息;StartClient
方法启动客户端连接,ReceiveMsg
和SendMsg
方法分别用于接收和发送消息,Send
方法将消息加入发送队列,Close
方法关闭套接字并发送退出消息,同时在脚本销毁时自动调用关闭操作
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class UDPNetMgr : MonoBehaviour
{
private static UDPNetMgr instance;
public static UDPNetMgr Instance => instance;
private EndPoint serverIpPoint;
public Socket socket;
private bool IsClose=true;
public byte[] cacheBytes = new byte[512];
//创建两个队列来收消息和发消息
public Queue<BaseMsg> sendMsgQueue = new Queue<BaseMsg>();
public Queue<BaseMsg> receiveMsgQueue = new Queue<BaseMsg>();
void Awake()
{
instance = this;
DontDestroyOnLoad(this.gameObject);
//创建两个线程来执行收发消息
ThreadPool.QueueUserWorkItem(ReceiveMsg);
ThreadPool.QueueUserWorkItem(SendMsg);
}
// Update is called once per frame
void Update()
{
if(receiveMsgQueue .Count >0)
{
BaseMsg basemsg = receiveMsgQueue.Dequeue();
switch (basemsg )
{
case PlayerMsg msg:
print(msg.playerID);
print(msg.playerData .atk);
print(msg.playerData .lev);
print(msg.playerData .name);
break;
}
}
}
//启动客户端Socket的方法
public void StartClient(string ip,int port)
{
if (!IsClose)
return;
//记录远端服务器的Ip和端口号
serverIpPoint = new IPEndPoint(IPAddress.Parse(ip), port);
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(ipPoint);
IsClose = false;
print("客户端网络启动");
}
catch (System.Exception e)
{
print("启动客户端Socket失败" + e.Message);
}
}
//接收消息的方法
public void ReceiveMsg(object obj)
{
EndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
int msgID;
int nowIndex;
int msgLength;
while (socket!=null&&!IsClose)
{
try
{
socket.ReceiveFrom(cacheBytes, ref ipEndPoint);
//为了避免非服务器发来的骚扰消息
if (!ipEndPoint.Equals(serverIpPoint))
continue;//如果不是服务器发来的消息,就不处理了
//处理服务器发来的消息
nowIndex = 0;
//解析ID
msgID = BitConverter.ToInt32(cacheBytes, nowIndex);
nowIndex += 4;
//解析消息长度
msgLength = BitConverter.ToInt32(cacheBytes, nowIndex);
nowIndex += 4;
//解析消息体
BaseMsg msg = null;
switch (msgID)
{
case 1001:
msg = new PlayerMsg();
//反序列化消息体
msg.Reading(cacheBytes, nowIndex);
break;
}
if (msg != null)
receiveMsgQueue.Enqueue(msg);
}
catch (SocketException s)
{
print("接收消息出问题" + s.SocketErrorCode + s.Message);
}
catch (System.Exception e)
{
print("接收消息出问题(非网络问题)" + e.Message);
}
}
}
//发送消息的方法
public void SendMsg(object obj)
{
while (socket!=null&&!IsClose)
{
if(sendMsgQueue .Count >0)
{
try
{
socket.SendTo(sendMsgQueue.Dequeue().Writing(), serverIpPoint);
}
catch (SocketException e)
{
print("发送消息失败" + e.SocketErrorCode + e.Message);
}
}
}
}
public void Send(BaseMsg msg)
{
sendMsgQueue.Enqueue(msg);
}
//关闭socket
public void Close()
{
if(socket!=null)
{
IsClose = true;
quitMsg quitMsg = new quitMsg();
//发送一个退出消息给服务器,让他移除记录
socket.SendTo(quitMsg.Writing(), serverIpPoint);
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
private void OnDestroy()
{
Close();
}
}
启动UDP客户端
MainUDP
类继承自MonoBehaviour
,是一个 Unity 脚本。它的主要功能是在游戏开始时确保UDPNetMgr
单例实例存在,然后启动 UDP 客户端并连接到指定的服务器地址和端口。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainUDP : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
if(UDPNetMgr .Instance ==null)
{
GameObject obj = new GameObject("UdpNet");
obj.AddComponent<UDPNetMgr>();
}
UDPNetMgr.Instance.StartClient(("127.0.0.1"), 8080);
}
// Update is called once per frame
void Update()
{
}
}
让客户端给服务器发消息
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lesson15 : MonoBehaviour
{
public Button btn;
// Start is called before the first frame update
void Start()
{
btn.onClick.AddListener(() =>
{
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.playerID = 1;
playerMsg.playerData.atk = 888;
playerMsg.playerData.lev = 999;
playerMsg.playerData.name = "DamnF客户端发来的消息";
UDPNetMgr.Instance.Send(playerMsg);
});
}
// Update is called once per frame
void Update()
{
}
}
实现客户端和服务器同步通信
运行服务器,启动客户端,使客户端和服务器实现UDP同步通信