上一篇简单的介绍了下Unity客户端和服务器的Socket通信,但是还不能实现多个客户端与服务器的通信,所以今天在这边把前面的工程完善一下(使用的是上篇讲到的UdpClient类来实现),实现多个客户端与服务器的udp通信。效果图如下,两个客户端可以向服务器发送消息,然后服务器每隔3秒给“连接上”的客户端发送收到的消息。
思路上一篇已经讲到过了,我们定义两个特殊的字段,来告知服务器,哪些客户端连接上来了,哪些客户端退出了,如下脚本,该脚本客户端服务器各存一份。
namespace Tool {
class SocketDefine {
public const int port = 8078;//端口号
public const string ip = "127.0.0.1";
public const string udpConnect = "udpConnect";//udp连接
public const string udpDisconnect = "udpDisconnect";//udp断开连接
}
}
服务器相比之前要做的就是,当接收到udpConnect信息时要记录下发该消息的客户端ip地址,保存在一个字典中,字典不为空的时候,每隔三秒给字典中存在的客户端发送消息(简单测试,目前服务器给各个客户端发的消息就是客户端发给服务器的最新消息)。当收到udpDisconnect消息时,将该客户端从字典中删除即可。同时我们定义一个新的类ClientMessage,用于存放客户端数据。具体代码如下:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Tool {
public class ClientMessage {
///
/// 每个客户端的唯一id,即ip地址+端口号
///
public string clientId;
///
/// 客户端地址信息
///
public IPEndPoint clientIPEndPoint;
///
/// 该客户端发送给服务器的最新消息
///
public string recieveMessage;
public ClientMessage(string id, IPEndPoint point, string msg) {
clientId = id;
clientIPEndPoint = new IPEndPoint(point.Address, point.Port);
recieveMessage = msg;
}
}
class UdpManager : SingleClass {
UdpClient m_udpClient;
IPEndPoint m_clientIpEndPoint;//存放客户端地址
Thread m_connectThread;//接收客户端消息的线程
byte[] m_result = new byte[1024];//存放接收到的消息
int m_sendCount;//发送次数
Dictionary m_clientMessageDic;//存放客户端信息,key->ip+port
//初始化
public void Start() {
IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(SocketDefine.ip), SocketDefine.port);
m_udpClient = new UdpClient(ipEnd);
m_clientMessageDic = new Dictionary();
m_sendCount = 0;
//定义客户端
m_clientIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Console.WriteLine("等待连接数据");
//开启一个线程连接
m_connectThread = new Thread(new ThreadStart(Receive));
m_connectThread.Start();
//定时器
System.Timers.Timer t = new System.Timers.Timer(3000);//实例化Timer类,设置间隔时间为3000毫秒
t.Elapsed += new System.Timers.ElapsedEventHandler(SendToClient);//到达时间的时候执行事件
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true)
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件
}
public void SendToClient(object source, System.Timers.ElapsedEventArgs e) {
Send(GetAllClientMessage());
}
public void Send(string data) {
if(m_clientMessageDic == null || m_clientMessageDic.Count == 0) {
return;
}
try {
NetBufferWriter writer = new NetBufferWriter();
writer.WriteString(data);
byte[] msg = writer.Finish();
foreach(var point in m_clientMessageDic) {
Console.WriteLine("send to " + point.Key + " " + data);
m_udpClient.Send(msg, writer.finishLength, point.Value.clientIPEndPoint);
}
m_sendCount++;
} catch(Exception ex) {
Console.WriteLine("send error " + ex.Message);
}
}
//服务器接收
void Receive() {
while(true) {
try {
m_result = new byte[1024];
m_result = m_udpClient.Receive(ref m_clientIpEndPoint);
NetBufferReader reader = new NetBufferReader(m_result);
string data = reader.ReadString();
string clientId = string.Format("{0}:{1}", m_clientIpEndPoint.Address, m_clientIpEndPoint.Port);
if(data.Equals(SocketDefine.udpConnect)) {
AddNewClient(clientId, new ClientMessage(clientId, m_clientIpEndPoint, data));
} else if(data.Equals(SocketDefine.udpDisconnect)) {
RemoveClient(clientId);
} else {
if(m_clientMessageDic != null && m_clientMessageDic.ContainsKey(clientId)) {
m_clientMessageDic[clientId].recieveMessage = data;
}
}
Console.WriteLine(m_clientIpEndPoint + " 数据内容:{0}", data);
} catch(Exception ex) {
Console.WriteLine("receive error " + ex.Message);
}
}
}
//连接关闭
void Close() {
//关闭线程
if(m_connectThread != null) {
m_connectThread.Interrupt();
//Thread abort is not supported on this platform.
//m_connectThread.Abort();
m_connectThread = null;
}
m_clientMessageDic.Clear();
if(m_udpClient != null) {
m_udpClient.Close();
m_udpClient.Dispose();
}
Console.WriteLine("断开连接");
}
void AddNewClient(string id, ClientMessage msg) {
if(m_clientMessageDic != null && !m_clientMessageDic.ContainsKey(id)) {
m_clientMessageDic.Add(id, msg);
}
}
void RemoveClient(string id) {
if(m_clientMessageDic != null && m_clientMessageDic.ContainsKey(id)) {
m_clientMessageDic.Remove(id);
}
}
string GetAllClientMessage() {
string allMsg = "m_sendCount " + m_sendCount + "\n";
foreach(var msg in m_clientMessageDic) {
allMsg += (msg.Value.clientId + "->" + msg.Value.recieveMessage + "\n");
}
return allMsg;
}
}
}
服务器就更简单啦,connect的时候给服务器发送一个udpConnect消息,断开的时候发送一个udpDisconnect消息即可。
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
namespace Tool {
public class UdpManager : SingleClass, ISocket {
public delegate void OnGetReceive(string message);//接收到消息的委托
public OnGetReceive onGetReceive;
byte[] m_result = new byte[1024];
Thread m_connectThread;
UdpClient m_udpClient;
IPEndPoint m_serverIPEndPoint;
IPEndPoint m_recieveIPEndPoint;
bool m_isConnected;
//是否已连接的标识
public bool IsConnected() {
return m_isConnected;
}
//udp是无连接的,所以方法里只做了一些初始化操作
public void Connect(string ip, int port) {
m_serverIPEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
m_udpClient = new UdpClient(0);//!!!!!主要一定要设置port为0,不然无法接收到服务器的消息
m_isConnected = true;
m_recieveIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
//开启一个线程连接
m_connectThread = new Thread(new ThreadStart(RecieveMessage));
m_connectThread.Start();
SendMessage(SocketDefine.udpConnect);
}
public void SendMessage(string data) {
if(IsConnected()) {
NetBufferWriter writer = new NetBufferWriter();
Debug.Log("SendMessage " + data);
writer.WriteString(data);
m_udpClient.Send(writer.Finish(), writer.finishLength, m_serverIPEndPoint);
}
}
public void Disconnect() {
SendMessage(SocketDefine.udpDisconnect);
if(m_connectThread != null) {
m_connectThread.Interrupt();
//在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。
m_connectThread.Abort();
}
if(IsConnected()) {
Debug.Log("Disconnect");
m_isConnected = false;
}
if(m_udpClient != null) {
m_udpClient.Close();
}
}
public void RecieveMessage() {
while(IsConnected()) {
try {
m_result = new byte[1024];
//m_udpClient的port不是0的时候,会报错,无效参数
m_result = m_udpClient.Receive(ref m_recieveIPEndPoint);
NetBufferReader reader = new NetBufferReader(m_result);
string msg = reader.ReadString();
Debug.Log("RecieveMessage " + msg);
if(onGetReceive != null) {
onGetReceive(msg);
}
} catch(Exception e) {
Debug.Log("recieve error " + e.Message);
}
}
}
}
}
这样就可以实现最简单的多客户端和服务器的udp通信了