日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Unity技術博客

 kiki的號 2017-07-22

Unity版本: 5.3

使用語言: C#


寫在前面

ProtoBuf是Google公司推出的一種二進制序列化工具,適用于數據的網絡傳輸。
基于Socket實現時時通信,關于數據粘包的編碼和解碼處理是必不可少的。


實現功能:

   1.基于ProtoBuf序列化對象
   2.使用Socket實現時時通信
   3.數據包的編碼和解碼

3.數據包的編碼和解碼

首先,舉個例子,這個月信用卡被媳婦刷爆了,面對房貸車貸的壓力,我只能選擇分期付款。。。

那么OK了,現在我想問一下,當服務器向客戶端發(fā)送的數據過大時怎么辦呢?

當服務器需要向客戶端發(fā)送一條很長的數據,也會“分期付款!”,服務器會把一條很長的數據分成若干條小數據,多次發(fā)送給客戶端。

可是,這樣就又有另外一個問題,客戶端接受到多條數據之后如何解析?

這里其實就是客戶端的解碼。server發(fā)數據一般采用“長度+內容”的格式,Client接收到數據之后,先提取出長度來,然后根據長度判斷內容是否發(fā)送完畢。

再次重申,用戶在發(fā)送序列化好的消息的前,需要先編碼后再發(fā)送消息;用戶在接受消息后,需要解碼之后再解析數據(反序列化)。

using UnityEngine;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// 編碼和解碼
/// </summary>
public class NetEncode {

    /// <summary>
    /// 將數據編碼 長度+內容
    /// </summary>
    /// <param name="data">內容</param>
    public static byte[] Encode(byte[] data)
    {
        //整形占四個字節(jié),所以聲明一個+4的數組
        byte[] result = new byte[data.Length + 4];
        //使用流將編碼寫二進制
        MemoryStream ms = new MemoryStream();
        BinaryWriter br = new BinaryWriter(ms);
        br.Write(data.Length);
        br.Write(data);
        //將流中的內容復制到數組中
        System.Buffer.BlockCopy(ms.ToArray(), 0, result, 0, (int)ms.Length);
        br.Close();
        ms.Close();
        return result;
    }

    /// <summary>
    /// 將數據解碼
    /// </summary>
    /// <param name="cache">消息隊列</param>
    public static byte[] Decode(ref List<byte> cache)
    {
        //首先要獲取長度,整形4個字節(jié),如果字節(jié)數不足4個字節(jié)
        if(cache.Count < 4)
        {
            return null;
        }
        //讀取數據
        MemoryStream ms = new MemoryStream(cache.ToArray());
        BinaryReader br = new BinaryReader(ms);
        int len = br.ReadInt32();
        //根據長度,判斷內容是否傳遞完畢
        if(len > ms.Length - ms.Position)
        {
            return null;
        }
        //獲取數據
        byte[] result = br.ReadBytes(len);
        //清空消息池
        cache.Clear();
        //講剩余沒處理的消息存入消息池
        cache.AddRange(br.ReadBytes((int)ms.Length - (int)ms.Position));

        return result;
    }
}

用戶接受數據代碼如下:

using System;
using System.Collections.Generic;
using System.Net.Sockets;

/// <summary>
/// 表示一個客戶端
/// </summary>
public class NetUserToken {
    //連接客戶端的Socket
    public Socket socket;
    //用于存放接收數據
    public byte[] buffer;
    //每次接受和發(fā)送數據的大小
    private const int size = 1024;

    //接收數據池
    private List<byte> receiveCache;
    private bool isReceiving;
    //發(fā)送數據池
    private Queue<byte[]> sendCache;
    private bool isSending;

    //接收到消息之后的回調
    public Action<NetModel> receiveCallBack;


    public NetUserToken()
    {
        buffer = new byte[size];
        receiveCache = new List<byte>();
        sendCache = new Queue<byte[]>();
    }

    /// <summary>
    /// 服務器接受客戶端發(fā)送的消息
    /// </summary>
    /// <param name="data">Data.</param>
    public void Receive(byte[] data)
    {
        UnityEngine.Debug.Log("接收到數據");
        //將接收到的數據放入數據池中
        receiveCache.AddRange(data);
        //如果沒在讀數據
        if(!isReceiving)
        {
            isReceiving = true;
            ReadData();
        }
    }

    /// <summary>
    /// 讀取數據
    /// </summary>
    private void ReadData()
    {
        byte[] data = NetEncode.Decode(ref receiveCache);
        //說明數據保存成功
        if(data != null)
        {
            NetModel item = NetSerilizer.DeSerialize(data);
            UnityEngine.Debug.Log(item.Message);
            if(receiveCallBack != null)
            {
                receiveCallBack(item);
            }
            //尾遞歸,繼續(xù)讀取數據
            ReadData();
        }
        else
        {
            isReceiving = false;
        }
    }

    /// <summary>
    /// 服務器發(fā)送消息給客戶端
    /// </summary>
    public void Send()
    {
        try {
            if (sendCache.Count == 0) {
                isSending = false;
                return;    
            }
            byte[] data = sendCache.Dequeue ();
            int count = data.Length / size;
            int len = size;
            for (int i = 0; i < count + 1; i++) {
                if (i == count) {
                    len = data.Length - i * size;
                }
                socket.Send (data, i * size, len, SocketFlags.None);
            }
            UnityEngine.Debug.Log("發(fā)送成功!");
            Send ();
        } catch (Exception ex) {
            UnityEngine.Debug.Log(ex.ToString());
        }
    }

    public void WriteSendDate(byte[] data){
        sendCache.Enqueue(data);
        if(!isSending)
        {
            isSending = true;
            Send();
        }
    }
}

寫在最后

 #成功的道路沒有捷徑,代碼這條路更是如此,唯有敲才是王道。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約