Unity网络服务器搭建【中高级】
来源:互联网 发布:淘宝如何办理发票 编辑:程序博客网 时间:2024/06/02 04:03
前言
众所周知,网络游戏中,服务器的搭建尤为重要,无论是授权服务器,还是非授权服务器,它都承担着很大一部分的数据处理。今天,给大家分享一个中高级的小服务器搭建,当然也是在Unity上实现的,可以迁移为服务,外部程序等。其中加入了对象的序列化与反序列化,数据包的封包与拆包,细细品味,别有一番滋味。
服务器基础搭建
using UnityEngine;using System.Collections;using System.Net;using System.Net.Sockets;using System.Collections.Generic;using System;using System.IO;//实时消息处理委托public delegate void NetEventHandler (string msg);public class NetUtility{ //单例 public static readonly NetUtility Instance = new NetUtility (); //消息回调 private NetEventHandler ReceiveCallback; //服务器Tcp private TcpListener tcpServer; //客户端Tcp private TcpClient tcpClient; //缓冲区 private byte[] buffer; //缓存数据组 private List<byte> cache; //网络节点 private IPEndPoint serverIPEndPoint; /// <summary> /// 设置网络节点 /// </summary> /// <param name="ep">网络节点.</param> public void SetIpAddressAndPort (IPEndPoint ep) { //只写网络节点 serverIPEndPoint = ep; } /// <summary> /// 设置委托 /// </summary> /// <param name="handler">消息委托.</param> public void SetDelegate (NetEventHandler handler) { //只写赋值回调 ReceiveCallback = handler; } /// <summary> /// Initializes a new instance of the <see cref="NetUtility"/> class. /// </summary> private NetUtility () { //服务器实例 tcpServer = new TcpListener (IPAddress.Any, 23456); //客户端实例 tcpClient = new TcpClient (AddressFamily.InterNetwork); //缓冲区初始化 buffer = new byte[1024]; //缓存数据组实例 cache = new List<byte> (); //默认网络节点 serverIPEndPoint = new IPEndPoint (IPAddress.Parse ("127.0.0.1"), 23456); }
服务器部分
#region Server Part:/// <summary>/// 开启服务器/// </summary>public void ServerStart (){ //开启服务器 tcpServer.Start (10); //服务器开启提示 ReceiveCallback ("Server Has Init!"); //开始异步接受客户端的连接请求 tcpServer.BeginAcceptTcpClient (AsyncAccept, null);}/// <summary>/// 异步连接回调/// </summary>/// <param name="ar">Ar.</param>void AsyncAccept (System.IAsyncResult ar){ //接受到客户端的异步连接请求 tcpClient = tcpServer.EndAcceptTcpClient (ar); //有新的客户端连接提示 ReceiveCallback ("Accept Client :" + tcpClient.Client.RemoteEndPoint.ToString ()); //异步接收消息 tcpClient.Client.BeginReceive (buffer, 0, 1024, SocketFlags.None, AsyncReceive, tcpClient.Client); //异步接受客户端请求尾递归 tcpServer.BeginAcceptTcpClient (AsyncAccept, null);}/// <summary>/// 异步接收消息回调/// </summary>/// <param name="ar">Ar.</param>void AsyncReceive (System.IAsyncResult ar){ //获取消息套接字 Socket workingClient = ar.AsyncState as Socket; //完成接收 int msgLength = workingClient.EndReceive (ar); //如果接收到了数据 if (msgLength > 0) { //消息接收提示 ReceiveCallback ("ReceiveData : " + msgLength + "bytes"); //临时缓冲区 byte[] tempBuffer = new byte[msgLength]; //拷贝数据到临时缓冲区 Buffer.BlockCopy (buffer, 0, tempBuffer, 0, msgLength); //数据放到缓存数据组队尾 cache.AddRange (tempBuffer); //拆包解析 byte[] result = LengthDecode (ref cache); //如果已经接收完全部数据 if (result != null) { //开始反序列化数据 NetModel resultModel = DeSerialize (result); //TODO:Object Processing! //数据对象结果提示 ReceiveCallback ("Object Result IP : " + resultModel.senderIp); ReceiveCallback ("Object Result Content : " + resultModel.content); ReceiveCallback ("Object Result Time : " + resultModel.time); } //消息未接收全,继续接收 tcpClient.Client.BeginReceive (buffer, 0, 1024, SocketFlags.None, AsyncReceive, tcpClient.Client); }}#endregion
客户端部分
#region Client Part/// <summary>/// 客户端连接/// </summary>public void ClientConnnect (){ //连接到服务器 tcpClient.Connect (serverIPEndPoint); //连接到服务器提示 ReceiveCallback ("Has Connect To Server : " + serverIPEndPoint.Address.ToString ());}/// <summary>/// 发送消息/// </summary>/// <param name="model">Model.</param>public void SendMsg (NetModel model){ //将数据对象序列化 buffer = Serialize (model); //将序列化后的数据加字节头 buffer = LengthEncode (buffer); //拆分数据,多次发送 for (int i = 0; i < buffer.Length/1024 + 1; i++) { //满发送,1KB int needSendBytes = 1024; //最后一次发送,剩余字节 if (i == buffer.Length / 1024) { //计算剩余字节 needSendBytes = buffer.Length - i * 1024; } //发送本次数据 tcpClient.GetStream ().Write (buffer, i * 1024, needSendBytes); }}#endregion
公共方法
#region Public Function/// <summary>/// 数据加字节头操作/// </summary>/// <returns>数据结果.</returns>/// <param name="data">源数据.</param>byte[] LengthEncode (byte[] data){ //内存流实例 using (MemoryStream ms = new MemoryStream()) { //二进制流写操作实例 using (BinaryWriter bw = new BinaryWriter(ms)) { //先写入字节长度 bw.Write (data.Length); //再写入所有数据 bw.Write (data); //临时结果 byte[] result = new byte[ms.Length]; //将写好的流数据放入临时结果 Buffer.BlockCopy (ms.GetBuffer (), 0, result, 0, (int)ms.Length); //返回临时结果 return result; } }}/// <summary>/// 数据解析,拆解字节头,获取数据./// </summary>/// <returns>源数据.</returns>/// <param name="cache">缓存数据.</param>byte[] LengthDecode (ref List<byte> cache){ //如果字节数小于4,出现异常 if (cache.Count < 4) return null; //内存流实例 using (MemoryStream ms = new MemoryStream(cache.ToArray())) { //二进制流读操作实例 using (BinaryReader br = new BinaryReader(ms)) { //先读取数据长度,一个int值 int realMsgLength = br.ReadInt32 (); //如果未接收全数据,下次继续接收 if (realMsgLength > ms.Length - ms.Position) { return null; } //接收完,读取所有数据 byte[] result = br.ReadBytes (realMsgLength); //清空缓存 cache.Clear (); //返回结果 return result; } }}/// <summary>/// 序列化数据./// </summary>/// <param name="mod">数据对象.</param>private byte[] Serialize (NetModel mod){ try { //内存流实例 using (MemoryStream ms = new MemoryStream()) { //ProtoBuf协议序列化数据对象 ProtoBuf.Serializer.Serialize<NetModel> (ms, mod); //创建临时结果数组 byte[] result = new byte[ms.Length]; //调整游标位置为0 ms.Position = 0; //开始读取,从0到尾 ms.Read (result, 0, result.Length); //返回结果 return result; } } catch (Exception ex) { Debug.Log ("Error:" + ex.ToString ()); return null; }}/// <summary>/// 反序列化数据./// </summary>/// <returns>数据对象.</returns>/// <param name="data">源数据.</param>private NetModel DeSerialize (byte[] data){ try { //内存流实例 using (MemoryStream ms = new MemoryStream(data)) { //调整游标位置 ms.Position = 0; //ProtoBuf协议反序列化数据 NetModel mod = ProtoBuf.Serializer.Deserialize<NetModel> (ms); //返回数据对象 return mod; } } catch (Exception ex) { Debug.Log ("Error: " + ex.ToString ()); return null; }}#endregion
服务器实现
using UnityEngine;using System.Collections;using System.IO;using System;/// <summary>/// 服务器实现./// </summary>public class ServerDemo : MonoBehaviour{ //临时消息接收 string currentMsg = ""; Vector2 scrollViewPosition; void Start () { scrollViewPosition = Vector2.zero; //消息委托 NetUtility.Instance.SetDelegate ((string msg) => { Debug.Log (msg); currentMsg += msg + "\r\n"; }); //开启服务器 NetUtility.Instance.ServerStart (); } void OnGUI () { scrollViewPosition = GUILayout.BeginScrollView (scrollViewPosition, GUILayout.Width (300), GUILayout.Height (300)); //消息展示 GUILayout.Label (currentMsg); GUILayout.EndScrollView (); }}
客户端实现
using UnityEngine;using System.Collections;using System.Text;using System;/// <summary>/// 客户端实现/// </summary>public class ClientDemo : MonoBehaviour{ //待解析地址 public string wwwAddress = ""; void Start () { //消息处理 NetUtility.Instance.SetDelegate ((string msg) => { Debug.Log (msg + "\r\n"); }); //连接服务器 NetUtility.Instance.ClientConnnect (); //开启协程 StartCoroutine (ServerStart ()); } IEnumerator ServerStart () { //加载网页数据 WWW www = new WWW (wwwAddress); yield return www; //编码获取内容 string content = UTF8Encoding.UTF8.GetString (www.bytes); //内容测试 Debug.Log (content); //待发送对象 NetModel nm = new NetModel (); //消息体 nm.senderIp = "127.0.0.1"; nm.content = content; nm.time = DateTime.Now.ToString (); //发送数据对象 NetUtility.Instance.SendMsg (nm); }}
Demo下载:链接: http://pan.baidu.com/s/1mhgUALy 密码: u36k
结束语
文中的ProtoBuf是跨平台的,可以跨平台传输对象。本文使用的是传输层的协议,有兴趣的同学可以使用网络层协议Socket实现。
0 0
- Unity网络服务器搭建【中高级】
- 为Unity搭建Web服务器
- Unity环境搭建Photon服务器
- Unity连接Photo服务器搭建环境
- Ubuntu网络服务器环境搭建
- 搭建RHEL6网络安装服务器(PXEBOOT+DHCP)
- 搭建个人家用NAS网络存储服务器
- 远程网络安装linux服务器搭建
- IOS网络学习-3、搭建本地服务器
- adsl网络花生壳如何搭建ftp服务器?
- CentOS搭建PHP服务器之联通网络
- linux 6.3搭建网络安装服务器
- 搭建多系统网络yum服务器
- 网络服务器的搭建(tftp,nfs)
- yum 网络库服务器基础搭建
- [Unity 网络]网络基础
- 《Android中高级工程师面试指南 — 网络协议和网络框架面试讲解 — OKHttp》
- Lighttpd+Squid+Apache搭建高效率Web网络服务器
- 算法——回文数
- 应对最新的勒索病毒的方法
- bzoj3653&cogs2211 谈笑风生
- 设计模式的总结二:创造型模式
- 好好吃饭的挑战100天
- Unity网络服务器搭建【中高级】
- 数据链路层三个问题---封装成帧、透明传输、差错检查
- 267. Palindrome Permutation II
- oracle基本命令集合(3)--分组与统计
- 【Java EE 】--JavaServer Faces 01
- 位异或运算符^的特点及面试题
- DPCM编码
- Linux 系统(ubuntu和CentOS)nginx服务器和php安装
- 数据结构(4)之递归