1、Socket的常用属性和方法
1.1 常用属性
1)套接字的连接状态
cs
socketTcp.Connected
2)获取套接字的类型
cs
socketTcp.SocketType
3)获取套接字的协议类型
cs
socketTcp.ProtocolType
4)获取套接字的寻址方案
cs
socketTcp.AddressFamily
5)从网络中获取准备读取的数据数据量
cs
socketTcp.Available
6)获取本机EndPoint对象(注意:IPEndPoint继承EndPoint)
cs
socketTcp.LocalEndPoint as IPEndPoint
7)获取远程EndPoint对象
cs
socketTcp.RemoteEndPoint as IPEndPoint;
1.2 同步常用方法
服务端和客户端操作流程一样
1)绑定IP和端口, Bind(ip地址和端口),绑定的是本机的
cs
IPEndPoint iPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(iPoint);
2)发送消息,SendTo
cs
socket.SendTo(Encoding.UTF8.GetBytes("欢迎发送消息给服务器"), remoteIpPoint2);
3)接受消息,ReceiveFrom
cs
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
4)释放连接并关闭socket,先于close调用
cs
socket.Shutdown(SocketShutdown.Both);
5)关闭连接,释放所有Socket管理资源
cs
socket.Close();
1.3 异步常用方法
1)发送消息
发送消息方式1,BeginSendTo和EndSendTo
cs
byte[] bytes = Encoding.UTF8.GetBytes("dsjkdjs13");
EndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.BeginSendTo(bytes, 0, bytes.Length, SocketFlags.None, ipPoint, SendToOver, socket);
private void SendToOver(IAsyncResult result)
{
try
{
Socket s = result.AsyncState as Socket;
s.EndSendTo(result);
print("发送成功");
}
catch (SocketException e)
{
print("发送失败" + e.SocketErrorCode + e.Message);
}
}
发送消息方式2, SendToAsync
cs
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
//设置要发送的数据
args.SetBuffer(bytes, 0, bytes.Length);
//设置完成事件
args.Completed += SendToAsync;
socket.SendToAsync(args);
private void SendToAsync(object s, SocketAsyncEventArgs args)
{
if(args.SocketError == SocketError.Success)
{
print("发送成功");
}
else
{
print("发送失败");
}
}
2)接收消息
接收消息方式1,BeginReceiveFrom和EndReceiveFrom
cs
socket.BeginReceiveFrom(cacheBytes, 0, cacheBytes.Length, SocketFlags.None, ref ipPoint, ReceiveFromOver, (socket, ipPoint));
private void ReceiveFromOver(IAsyncResult result)
{
try
{
(Socket s, EndPoint ipPoint) info = ((Socket, EndPoint))result.AsyncState;
//返回值 就是接受了多少个 字节
int num = info.s.EndReceiveFrom(result, ref info.ipPoint);
//处理消息
//处理完消息 又继续接受消息
info.s.BeginReceiveFrom(cacheBytes, 0, cacheBytes.Length, SocketFlags.None, ref info.ipPoint, ReceiveFromOver, info);
}
catch (SocketException e)
{
print("接受消息出问题" + e.SocketErrorCode + e.Message);
}
}
接收消息方式2,ReceiveFromAsync
cs
SocketAsyncEventArgs args2 = new SocketAsyncEventArgs();
//设置接收消息的容器
args2.SetBuffer(cacheBytes, 0, cacheBytes.Length);
args2.Completed += ReceiveFromAsync;
socket.ReceiveFromAsync(args2);
private void ReceiveFromAsync(object s, SocketAsyncEventArgs args)
{
if(args.SocketError != SocketError.Success)
{
print("接受成功");
//具体收到多少个字节
//args.BytesTransferred
//可以通过以下两种方式获取到收到的字节数组内容
//args.Buffer
//cacheBytes
//解析消息
Socket socket = s as Socket;
//只需要设置 从第几个位置开始接受 能接几个
args.SetBuffer(0, args.Buffer.Length);
socket.ReceiveFromAsync(args);
}
else
{
print("接受失败");
}
}
2、Socket实现UDP通讯流程
2.1 服务端
1)创建套接字
cs
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
2)绑定本机地址
cs
IPEndPoint iPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(iPoint);
3)接受消息
cs
byte[] bytes = new byte[512];
//这个变量主要用来记录 谁发的消息 传入函数后 在内部 会帮助我们赋值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
4)发送消息到指定目标
cs
socket.SendTo(Encoding.UTF8.GetBytes("欢迎发送消息给服务器"), remoteIpPoint2);
5)释放关闭
cs
socket.Shutdown(SocketShutdown.Both);
socket.Close();
2.2 客户端
与服务端一样流程
3、简单实现UDP通讯完整代码
3.1 消息类型区分代码
1)BaseData
cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
public abstract class BaseData
{
/// <summary>
/// 用于子类重写的 获取字节数组容器大小的方法
/// </summary>
/// <returns></returns>
public abstract int GetBytesNum();
/// <summary>
/// 把成员变量序列化为 对应的字节数组
/// </summary>
/// <returns></returns>
public abstract byte[] Writing();
/// <summary>
/// 把二进制字节数组 反序列化 到成员变量当中
/// </summary>
/// <param name="bytes">反序列化使用的字节数组</param>
/// <param name="beginIndex">从该字节数组的第几个位置开始解析 默认是 0</param>
public abstract int Reading(byte[] bytes, int beginIndex = 0);
/// <summary>
/// 存储int类型变量到指定的字节数组
/// </summary>
/// <param name="bytes">指定字节数组</param>
/// <param name="value">具体的int值</param>
/// <param name="index">每次存储后用于记录当前索引位置的变量</param>
protected void WriteInt(byte[] bytes, int value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(int);
}
protected void WriteShort(byte[] bytes, short value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(short);
}
protected void WriteLong(byte[] bytes, long value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(long);
}
protected void WriteFloat(byte[] bytes, float value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(float);
}
protected void WriteByte(byte[] bytes, byte value, ref int index)
{
bytes[index] = value;
index += sizeof(byte);
}
protected void WriteBool(byte[] bytes, bool value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, (int)index);
index += sizeof(bool);
}
protected void WriteString(byte[] bytes, string value, ref int index)
{
//先存储string字节数组长度
byte[] strBytes = Encoding.UTF8.GetBytes(value);
int num = strBytes.Length;
BitConverter.GetBytes(num).CopyTo(bytes, index);
index += sizeof(int);
//在存储 string 字节数组
strBytes.CopyTo(bytes, index);
index += num;
}
protected void WriteData(byte[] bytes, BaseData data, ref int index)
{
data.Writing().CopyTo(bytes, index);
index += data.GetBytesNum();
}
/// <summary>
/// 根据字节数组读取整形
/// </summary>
/// <param name="bytes">读取数组</param>
/// <param name="index">开始读取的索引位置</param>
/// <returns></returns>
protected int ReadInt(byte[] bytes, ref int index)
{
int value = BitConverter.ToInt32(bytes, index);
index += sizeof(int);
return value;
}
protected short ReadShort(byte[] bytes, ref int index)
{
short value = BitConverter.ToInt16(bytes, index);
index += sizeof(short);
return value;
}
protected long ReadLong(byte[] bytes, ref int index)
{
long value = BitConverter.ToInt64(bytes, index);
index += sizeof(long);
return value;
}
protected float ReadFloat(byte[] bytes, ref int index)
{
float value = BitConverter.ToSingle(bytes, index);
index += sizeof(float);
return value;
}
protected byte ReadByte(byte[] bytes, ref int index)
{
byte b = bytes[index];
index++;
return b;
}
protected bool ReadBool(byte[] bytes, ref int index)
{
bool value = BitConverter.ToBoolean(bytes, index);
index += sizeof(bool);
return value;
}
protected string ReadString(byte[] bytes, ref int index)
{
int length = BitConverter.ToInt32(bytes, index);
index += sizeof(int);
string value = Encoding.UTF8.GetString(bytes, index, length);
index += length;
return value;
}
protected T ReadData<T>(byte[] bytes, ref int index) where T : BaseData, new()
{
T value = new T();
index += value.Reading(bytes, index);
return value;
}
}
2)BaseMsg
cs
using System.Collections;
using System.Collections.Generic;
public class BaseMsg : BaseData
{
public override int GetBytesNum()
{
throw new System.NotImplementedException();
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
throw new System.NotImplementedException();
}
public override byte[] Writing()
{
throw new System.NotImplementedException();
}
public virtual int GetID()
{
return 0;
}
}
3)PlayerData
cs
using System.Collections;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 玩家数据类
/// </summary>
public class PlayerData : BaseData
{
public string name;
public int atk;
public int lev;
public override int GetBytesNum()
{
return 4 + 4 + 4 + Encoding.UTF8.GetBytes(name).Length;
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
int index = beginIndex;
name = ReadString(bytes, ref index);
atk = ReadInt(bytes, ref index);
lev = ReadInt(bytes, ref index);
return index - beginIndex;
}
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
WriteString(bytes, name, ref index);
WriteInt(bytes, atk, ref index);
WriteInt(bytes, lev, ref index);
return bytes;
}
}
4)PlayerMsg
cs
using System.Collections;
using System.Collections.Generic;
public class PlayerMsg : BaseMsg
{
public int playerID;
public PlayerData playerData;
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
//先写消息ID
WriteInt(bytes, GetID(), ref index);
//写消息的成员变量
WriteInt(bytes, playerID, ref index);
WriteData(bytes, playerData, ref index);
return bytes;
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
//反序列化不需要取解析ID 因为在这一步之前 就应该把ID反序列化出来
//用来判断到底使用哪一个自定义类来反序列化
int index = beginIndex;
playerID = ReadInt(bytes, ref index);
playerData = ReadData<PlayerData>(bytes, ref index);
return index - beginIndex;
}
public override int GetBytesNum()
{
return 4 + //消息ID长度
4 + //palyerID
playerData.GetBytesNum(); //playerData
}
/// <summary>
/// 自定义的消息ID 主要用于区分是哪一个消息类
/// </summary>
/// <returns></returns>
public override int GetID()
{
return 1001;
}
}
5)QuitMsg
cs
using System.Collections;
using System.Collections.Generic;
public class QuitMsg : BaseMsg
{
public override int GetBytesNum()
{
return 8;
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
return 0;
}
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
WriteInt(bytes, GetID(), ref index);
WriteInt(bytes, 0, ref index);
return bytes;
}
public override int GetID()
{
return 1003;
}
}
6)HeartMsg
cs
using System.Collections;
using System.Collections.Generic;
public class HeartMsg : BaseMsg
{
public override int GetBytesNum()
{
return 8;
}
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
WriteInt(bytes, GetID(), ref index);
WriteInt(bytes, 0, ref index);
return bytes;
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
return 0;
}
public override int GetID()
{
return 999;
}
}
3.2 服务端
1)Client
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TeachUdpServerExercises
{
//用于记录和服务器通信过的客户端的IP和端口
class Client
{
public IPEndPoint clientIPandPort;
public string clientStrID;
//上一次收到消息的时间
public long frontTime = -1;
public Client(string ip, int port)
{
//规则和外面一样 记录唯一ID 通过IP+端口拼接的形式
clientStrID = ip + port;
//记录客户端信息
clientIPandPort = new IPEndPoint(IPAddress.Parse(ip), port);
}
public void ReceiveMsg(byte[] bytes)
{
//为了避免处理消息时 又 接受到其他消息 所以需要在处理之前 先把信息拷贝出来
//处理消息和接受信息 用不同的容器 避免出现问题
byte[] cacheBytes = new byte[512];
bytes.CopyTo(cacheBytes, 0);
//记录收到消息的 系统时间 单位为秒
frontTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
ThreadPool.QueueUserWorkItem(ReceiveHandle, cacheBytes);
}
//多线程处理消息
private void ReceiveHandle(object obj)
{
try
{
//取出传进来的字节
byte[] bytes = obj as byte[];
int nowIndex = 0;
//先处理 ID
int msgID = BitConverter.ToInt32(bytes, nowIndex);
nowIndex += 4;
//再处理 长度
int msgLength = BitConverter.ToInt32(bytes, nowIndex);
nowIndex += 4;
//再解析消息体
switch (msgID)
{
case 1001:
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.Reading(bytes, nowIndex);
Console.WriteLine(playerMsg.playerID);
Console.WriteLine(playerMsg.playerData.name);
Console.WriteLine(playerMsg.playerData.atk);
Console.WriteLine(playerMsg.playerData.lev);
break;
case 1003:
QuitMsg quitMsg = new QuitMsg();
//由于它没有消息体 所以不用反序列化
//quitMsg.Reading(bytes, nowIndex);
//处理退出
Program.serverSocket.RemoveClient(clientStrID);
break;
default:
break;
}
}
catch (Exception e)
{
Console.WriteLine("处理消息是出错" + e.Message);
//如果出错就不用记录这个客户端信息
Program.serverSocket.RemoveClient(clientStrID);
}
}
}
}
2)ServerSocket
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TeachUdpServerExercises
{
class ServerSocket
{
private Socket socket;
private bool isClose;
//我们可以通过记录谁给我发了消息 把它的 IP和端口记下来 这样就认为它是我的客户端
private Dictionary<string, Client> clientDic = new Dictionary<string, Client>();
public void Start(string ip, int port)
{
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);
//声明一个用于UDP通信的Socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
socket.Bind(ipPoint);
isClose = false;
//消息接受的处理
ThreadPool.QueueUserWorkItem(ReceiveMsg);
//定时检测超时线程
ThreadPool.QueueUserWorkItem(CheckTimeOut);
}
catch (Exception e)
{
Console.WriteLine("UDP开启出错" + e.Message);
}
}
private void CheckTimeOut(object obj)
{
long nowTime = 0;
List<string> delList = new List<string>();
while (true)
{
//每30秒检测一次 是否移除长时间没有接受到消息的客户端信息
Thread.Sleep(30000);
//当前系统时间
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
foreach (Client c in clientDic.Values)
{
//超过10秒没有收到消息的客户端 需要被移除
if(nowTime - c.frontTime >= 10)
{
delList.Add(c.clientStrID);
}
}
//从待删除列表中移除 超时的客户端信息
for (int i = 0; i < delList.Count; i++)
RemoveClient(delList[i]);
delList.Clear();
}
}
private void ReceiveMsg(object obj)
{
//接受消息的容器
byte[] bytes = new byte[512];
//记录谁发的
EndPoint ipPoint = new IPEndPoint(IPAddress.Any, 0);
//用于拼接字符串 唯一ID 是由 IP + 端口构成
string strID = "";
string ip;
int port;
while (!isClose)
{
if(socket.Available > 0)
{
lock(socket)
socket.ReceiveFrom(bytes, ref ipPoint);
//处理消息 最好不要在这直接处理 而是交给 客户端对象处理
//收到消息时 我们可以来判断 是不是记录了这个客户端信息 (IP和端口)
//取出发送的 IP和端口
ip = (ipPoint as IPEndPoint).Address.ToString();
port = (ipPoint as IPEndPoint).Port;
strID = ip + port;//拼接成一个唯一ID
//判断有没有记录这个客户端信息 如果有 用它直接处理消息
if (clientDic.ContainsKey(strID))
clientDic[strID].ReceiveMsg(bytes);
//如果没有 直接添加并处理消息
else
{
clientDic.Add(strID, new Client(ip, port));
clientDic[strID].ReceiveMsg(bytes);
}
}
}
}
//指定发送一个消息给某个目标
public void SendTo(BaseMsg msg, IPEndPoint ipPoint)
{
try
{
lock(socket)
socket.SendTo(msg.Writing(), ipPoint);
}
catch (SocketException s)
{
Console.WriteLine("发消息出现问题" + s.SocketErrorCode + s.Message);
}
catch(Exception e)
{
Console.WriteLine("发消息出现问题(可能是序列化问题)" + e.Message);
}
}
public void Broadcast(BaseMsg msg)
{
//广播消息 给谁广播
foreach (Client c in clientDic.Values)
{
SendTo(msg, c.clientIPandPort);
}
}
public void Close()
{
if(socket != null)
{
isClose = true;
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
}
public void RemoveClient(string clientID)
{
if (clientDic.ContainsKey(clientID))
{
Console.WriteLine("客户端{0}被移除了", clientDic[clientID].clientIPandPort);
clientDic.Remove(clientID);
}
}
}
}
3)启动代码
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TeachUdpServerExercises
{
class Program
{
public static ServerSocket serverSocket;
static void Main(string[] args)
{
#region UDP服务器要求
//如同TCP通信一样让UDP服务端可以服务多个客户端
//需要具备的功能有:
//1.区分消息类型(不需要处理分包、黏包)
//2.能够接受多个客户端的消息
//3.能够主动给发送过消息给自己的客户端发消息(记录客户端信息)
//4.主动记录上一次收到客户端消息的时间,如果长时间没有收到消息,主动移除记录的客户端信息
// 分析:
//1.UDP是无连接的,我们如何记录连入的客户端?
//2.UDP收发消息都是通过一个Socket来进行处理,我们应该如何处理收发消息?
//3.如果不使用心跳消息,我们如何记录上次收到消息的时间?
serverSocket = new ServerSocket();
serverSocket.Start("127.0.0.1", 8080);
Console.WriteLine("UDP服务器启动了");
while (true)
{
string input = Console.ReadLine();
if(input.Substring(0, 2) == "B:")
{
PlayerMsg msg = new PlayerMsg();
msg.playerData = new PlayerData();
msg.playerID = 100;
msg.playerData.name = "帅哥的UDP服务器";
msg.playerData.atk = 999;
msg.playerData.lev = 100;
serverSocket.Broadcast(msg);
}
}
#endregion
}
}
}
3.3 客户端
1)UdpNetMgr
cs
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;
private Socket socket;
//客户端Socket是否关闭
private bool isClose = true;
//两个容器 队列
//接受和发送消息的队列 在多线程里面可以操作
private Queue<BaseMsg> sendQueue = new Queue<BaseMsg>();
private Queue<BaseMsg> receiveQueue = new Queue<BaseMsg>();
private byte[] cacheBytes = new byte[512];
private void Awake()
{
instance = this;
DontDestroyOnLoad(gameObject);
}
private void Update()
{
if(receiveQueue.Count > 0)
{
BaseMsg baseMsg = receiveQueue.Dequeue();
switch (baseMsg)
{
case PlayerMsg msg:
print(msg.playerID);
print(msg.playerData.name);
print(msg.playerData.atk);
print(msg.playerData.lev);
break;
}
}
}
/// <summary>
/// 启动客户端socket相关方法
/// </summary>
/// <param name="ip">远端服务器的IP</param>
/// <param name="port">远端服务器的端口</param>
public void StartClient(string ip, int port)
{
//如果当前是开启状态 就不用在开启了
if (!isClose)
return;
//先记录服务器地址,后面发送消息会使用
serverIpPoint = new IPEndPoint(IPAddress.Parse(ip), port);
IPEndPoint clientIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(clientIpPoint);
isClose = false;
print("客户端网络启动");
ThreadPool.QueueUserWorkItem(ReceiveMsg);
ThreadPool.QueueUserWorkItem(SendMsg);
}
catch (System.Exception e)
{
print("启动Socket出问题" + e.Message);
}
}
private void ReceiveMsg(object obj)
{
EndPoint tempIpPoint = new IPEndPoint(IPAddress.Any, 0);
int nowIndex;
int msgID;
int msgLength;
while (!isClose)
{
if(socket != null && socket.Available > 0)
{
try
{
socket.ReceiveFrom(cacheBytes, ref tempIpPoint);
//为了避免处理 非服务器发来的 骚扰消息
if (!tempIpPoint.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)
receiveQueue.Enqueue(msg);
}
catch (SocketException s)
{
print("接受消息出问题" + s.SocketErrorCode + s.Message);
}
catch (Exception e)
{
print("接受消息出问题(非网络问题)" + e.Message);
}
}
}
}
private void SendMsg(object obj)
{
while (!isClose)
{
if (socket != null && sendQueue.Count > 0)
{
try
{
socket.SendTo(sendQueue.Dequeue().Writing(), serverIpPoint);
}
catch (SocketException e)
{
print("发送消息出错" + e.SocketErrorCode + e.Message);
}
}
}
}
//发送消息
public void Send(BaseMsg msg)
{
sendQueue.Enqueue(msg);
}
//关闭socket
public void Close()
{
if(socket != null)
{
//发送一个退出消息给服务器 让其移除记录
QuitMsg msg = new QuitMsg();
socket.SendTo(msg.Writing(), serverIpPoint);
isClose = true;
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
}
private void OnDestroy()
{
Close();
}
}
2)启动代码
cs
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()
{
}
}
3)消息发送测试代码


cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lesson15 : MonoBehaviour
{
public Button btnSend;
// Start is called before the first frame update
void Start()
{
btnSend.onClick.AddListener(() =>
{
PlayerMsg msg = new PlayerMsg();
msg.playerData = new PlayerData();
msg.playerID = 1;
msg.playerData.name = "小周的客户端";
msg.playerData.atk = 999;
msg.playerData.lev = 100;
UdpNetMgr.Instance.Send(msg);
});
}
// Update is called once per frame
void Update()
{
}
}