简介:采用UDP协议,利用Client Server架构(简称C/S架构)进行客户端对服务器的通信,利用服务器间接实现各客户端之间的通信。
注意:Unity发布时,需进行设置,将后台运行打开:PlayerSetting--->Resolution
--->Run in Background
需要的类
一、消息类:用于存放需要发送的信息(包括消息类型、发送者名字、消息内容),并对外提供消息类转为字节数组的方法、字节数组转为消息类的方法
——消息类型用枚举分为客户端上线消息,客户端下线消息,普通消息
——发送的信息用字符串进行发送
————对于字符串的处理(数据协议):
1)编码
2)写入字节数组的长度(4字节int型)
3)写入字节数组
using System;
using System.IO;
using System.Text;
namespace NetWork
{
public enum MessageType
{
//上线
Online,
//下线
Offline,
//常规
General
}
///<summary>
///聊天消息
///<summary>
public class ChatMessage
{
public MessageType Type { get; set; }
public string SenderName { get; set; }
public string Content { get; set; }
/// <summary>
/// 对象转换为字节数组
/// </summary>
/// <returns>byte[]</returns>
public byte[] ObjectToBytes()
{
//对象变成字节数组 string/int/bool - 二进制写入器->内存流MemroyStream-》 byte[]
using (MemoryStream stream = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(stream);
WriteString(writer, Type.ToString());
WriteString(writer, SenderName);
WriteString(writer, Content);
return stream.ToArray();
}
}
private void WriteString(BinaryWriter writer,string str)
{
byte[] typeBTS = Encoding.Unicode.GetBytes(str);
//写入长度
writer.Write(typeBTS.Length);
//写入内容
writer.Write(typeBTS);
}
/// <summary>
/// 将字节数组转换为对象
/// </summary>
/// <param name="bytes"></param>
/// <returns>ChatMessage</returns>
public static ChatMessage BytesToObject(byte[] bytes)
{
//创建聊天消息对象
ChatMessage obj = new ChatMessage();
//byte[] --内存流--二进制读取器--》string/int/bool
using(MemoryStream stream = new MemoryStream(bytes))
{
BinaryReader reader = new BinaryReader(stream);
string strType = ReadString(reader);
obj.Type = (MessageType)Enum.Parse(typeof(MessageType), strType);
obj.SenderName = ReadString(reader);
obj.Content = ReadString(reader);
return obj;
}
}
private static string ReadString(BinaryReader reader)
{
int Length = reader.ReadInt32();//自动读4个字节
byte[] BTS = reader.ReadBytes(Length);
return Encoding.Unicode.GetString(BTS);
}
}
}
二、客户端部分
1、消息发送、接收方法服务类
主要对外提供发送消息的方法,同时开辟线程进行接收消息。利用委托执行接收消息后的消息展示等方法的调用
这是实现通信的主要方法类,做成单例并在跨场景时不销毁。
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace NetWork
{
///<summary>
///
///<summary>
public class ClientUdpNetworkService : MonoSingleton<ClientUdpNetworkService>
{
private UdpClient udpService;
private Thread threadReceive;
//创建Socket对象(由登录窗口传递服务端Ip,端口)
public event EventHandler<MessageArriveEventArgs> MessageArrive;
public void Initialized(string severIP, int serverPort)
{
//跨场景不销毁当前游戏对象
DontDestroyOnLoad(this.gameObject);
//创建服务端终结点
IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(severIP), serverPort);
udpService = new UdpClient();//随机分配可用的端口
//与服务端建立连接(没有三次握手)
udpService.Connect(serverEP);
threadReceive = new Thread(ReceiveChatMessage);
threadReceive.Start();
//发送上线消息
NotifyServer(MessageType.Online);
}
private void NotifyServer(MessageType type)
{
SendChatMessage(new ChatMessage() { Type = type});
}
//发送数据
public void SendChatMessage(ChatMessage msg)
{
byte[] buffer = msg.ObjectToBytes();
//备注:发送时不能指定中断(创建Socket对象时,建立了链接)
udpService.Send(buffer,buffer.Length);
}
//接收数据
private void ReceiveChatMessage()
{
while (true)
{
IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udpService.Receive(ref remote);
ChatMessage msg = ChatMessage.BytesToObject(data);
//引发事件
if (MessageArrive == null) continue;
MessageArriveEventArgs args = new MessageArriveEventArgs
{
ArriveTime = DateTime.Now,
Message = msg
};
//在主线程中引用委托方法
CrossAssistance.Instance.ExecuteOnMainThread(() => { MessageArrive(this, args); });
}
}
//引发数据(1、事件参数类2、委托3、声明事件4、引发)
private void OnApplicationQuit()
{
//发送下线消息
NotifyServer(MessageType.Offline);
threadReceive.Abort();
udpService.Close();
}
}
}
2、消息发送、与接收脚本:调用“1”方法,注册“1”中的委托事件。
using UnityEngine;
using NetWork;
using System;
public class ChatClient:MonoBehaviour
{
private void Start()
{
//通过委托再接收到信息时执行
//注册事件
ClientUdpNetworkService.Instance.MessageArrive += DisplayMessage;
}
//显示数据
private void DisplayMessage(object sender, MessageArriveEventArgs args)
{
ChatMessage msg = args.Message;
DateTime dateTime = args.ArriveTime;
}
private void OnSendMessageButtonClick()
{
ChatMessage message = new ChatMessage()
{
Type = MessageType.General,
SenderName = "……",
Content = "……"
};
ClientUdpNetworkService.Instance.SendChatMessage(message);
}
}
三、服务端部分
1、消息发送、接收方法服务类
类似客户端部分,添加客户端列表,实现将所收到的消息转发给客户端
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace NetWork
{
///<summary>
///
///<summary>
public class ServerUdpNetworkService : MonoSingleton<ServerUdpNetworkService>
{
private UdpClient udpService;
private Thread threadReceive;
//创建Socket对象(由登录窗口传递服务端Ip,端口)
public event EventHandler<MessageArriveEventArgs> MessageArrive;
private List<IPEndPoint> allClientEP;
public override void Init()
{
base.Init();
allClientEP = new List<IPEndPoint>();
}
public void Initialized(string severIP, int serverPort)
{
//跨场景不销毁当前游戏对象
DontDestroyOnLoad(this.gameObject);
//创建服务端终结点
IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(severIP), serverPort);
udpService = new UdpClient(serverEP);//随机分配可用的端口
threadReceive = new Thread(ReceiveChatMessage);
threadReceive.Start();
}
//发送数据
public void SendChatMessage(ChatMessage msg, IPEndPoint remote)
{
byte[] buffer = msg.ObjectToBytes();
udpService.Send(buffer, buffer.Length, remote);
}
//接收数据
private void ReceiveChatMessage()
{
while (true)
{
IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udpService.Receive(ref remote);
ChatMessage msg = ChatMessage.BytesToObject(data);
//根据消息类型,执行相关逻辑
OnMessageArrived(msg,remote);
//引发事件
if (MessageArrive == null) continue;
MessageArriveEventArgs args = new MessageArriveEventArgs
{
ArriveTime = DateTime.Now,
Message = msg
};
//在主线程中引用委托方法
CrossAssistance.Instance.ExecuteOnMainThread(() => { MessageArrive(this, args); });
}
}
//引发数据(1、事件参数类2、委托3、声明事件4、引发)
private void OnApplicationQuit()
{
threadReceive.Abort();
udpService.Close();
}
private void OnMessageArrived(ChatMessage msg,IPEndPoint remote)
{
switch (msg.Type)
{
case MessageType.Offline:
//删除客户端
allClientEP.Remove(remote);
break;
case MessageType.Online:
//添加客户端
allClientEP.Add(remote);
break;
case MessageType.General:
//转发(需要客户端的终结点)
//foreach (var item in allClientEP)
// SendChatMessage(msg,item);
allClientEP.ForEach(item => SendChatMessage(msg, item));
break;
}
}
}
}
2、消息接收、显示脚本
与客户端部分一样
using UnityEngine;
using NetWork;
using System;
public class ChatServer : MonoBehaviour
{
private void Start()
{
//通过委托再接收到信息时执行
//注册事件
ServerUdpNetworkService.Instance.MessageArrive += DisplayMessage;
}
//显示数据
private void DisplayMessage(object sender, MessageArriveEventArgs args)
{
ChatMessage msg = args.Message;
DateTime dateTime = args.ArriveTime;
}
}
四、注册类脚本(客户端注册和服务端注册)
初始化,使客户端和服务端进行链接
public class GameMain : MonoBehaviour
{
public string serverIp;
public int Port;
//注册
private void OnEnterClientButtonClick()
{
ClientUdpNetworkService.Instance.Initialized(serverIp,Port);
}
private void OnEnterServerButtonClick()
{
ServerUdpNetworkService.Instance.Initialized(serverIp, Port);
}
}