Unity网络服务器搭建【中高级】

前言

众所周知,网络游戏中,服务器的搭建尤为重要,无论是授权服务器,还是非授权服务器,它都承担着很大一部分的数据处理。今天,给大家分享一个中高级的小服务器搭建,当然也是在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 cache;
          //网络节点
          private IPEndPoint serverIPEndPoint;
    
          /// 
          /// 设置网络节点
          /// 
          /// 网络节点.
          public void SetIpAddressAndPort (IPEndPoint ep)
          {
              //只写网络节点
              serverIPEndPoint = ep;
          }
          /// 
          /// 设置委托
          /// 
          /// 消息委托.
          public void SetDelegate (NetEventHandler handler)
          {
              //只写赋值回调
              ReceiveCallback = handler;
          }
          /// 
          /// Initializes a new instance of the  class.
          /// 
          private NetUtility ()
          {
              //服务器实例
              tcpServer = new TcpListener (IPAddress.Any, 23456);
              //客户端实例
              tcpClient = new TcpClient (AddressFamily.InterNetwork);
              //缓冲区初始化
              buffer = new byte[1024];
              //缓存数据组实例
              cache = new List ();
              //默认网络节点
              serverIPEndPoint = new IPEndPoint (IPAddress.Parse ("127.0.0.1"), 23456);
          }
    
  • 服务器部分

      #region Server Part:
      /// 
      /// 开启服务器
      /// 
      public void ServerStart ()
      {
          //开启服务器
          tcpServer.Start (10);
          //服务器开启提示
          ReceiveCallback ("Server Has Init!");
          //开始异步接受客户端的连接请求
          tcpServer.BeginAcceptTcpClient (AsyncAccept, null);
      }
      /// 
      /// 异步连接回调
      /// 
      /// Ar.
      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);
      }
      /// 
      /// 异步接收消息回调
      /// 
      /// Ar.
      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
      /// 
      /// 客户端连接
      /// 
      public void ClientConnnect ()
      {
          //连接到服务器
          tcpClient.Connect (serverIPEndPoint);
          //连接到服务器提示
          ReceiveCallback ("Has Connect To Server : " + serverIPEndPoint.Address.ToString ());
      }
      /// 
      /// 发送消息
      /// 
      /// Model.
      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
      /// 
      /// 数据加字节头操作
      /// 
      /// 数据结果.
      /// 源数据.
      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;
              }
          }
      }
      /// 
      /// 数据解析,拆解字节头,获取数据.
      /// 
      /// 源数据.
      /// 缓存数据.
      byte[] LengthDecode (ref List 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;
              }
          }
      }
      /// 
      /// 序列化数据.
      /// 
      /// 数据对象.
      private byte[] Serialize (NetModel mod)
      {
          try {
              //内存流实例
              using (MemoryStream ms = new MemoryStream()) {
                  //ProtoBuf协议序列化数据对象
                  ProtoBuf.Serializer.Serialize (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;
          }
      }
      
      /// 
      /// 反序列化数据.
      /// 
      /// 数据对象.
      /// 源数据.
      private NetModel DeSerialize (byte[] data)
      {
          try {
              //内存流实例
              using (MemoryStream ms = new MemoryStream(data)) {
                  //调整游标位置
                  ms.Position = 0;
                  //ProtoBuf协议反序列化数据
                  NetModel mod = ProtoBuf.Serializer.Deserialize (ms);
                  //返回数据对象
                  return mod;
                  
              }
          } catch (Exception ex) {
              Debug.Log ("Error: " + ex.ToString ());
              return null;
          }
      }
      #endregion
    
  • 服务器实现

      using UnityEngine;
      using System.Collections;
      using System.IO;
      using System;
    
      /// 
      /// 服务器实现.
      /// 
      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;
      /// 
      /// 客户端实现
      /// 
      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);
          }
      }
    
  • 结果图


    Unity网络服务器搭建【中高级】_第1张图片
    结果图
  • Demo下载:链接: http://pan.baidu.com/s/1mhgUALy 密码: u36k

结束语

文中的ProtoBuf是跨平台的,可以跨平台传输对象。本文使用的是传输层的协议,有兴趣的同学可以使用网络层协议Socket实现,本文后续还会有网络游戏传输协议的内容,同学们随时关注。

你可能感兴趣的:(Unity网络服务器搭建【中高级】)