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
原创粉丝点击