zoukankan      html  css  js  c++  java
  • 网络游戏的前后端通讯(二)

    【旧博客转移 - 2015年9月20日 22:48】

    1.Socket的粘包拆包

    tcp协议是有缓冲区的,如果发送的数据太小,会被放置缓冲区里,累积更多的数据再一起发送,发送后不会立马清除缓冲区,等待TCP应答消息到了,才会清除缓冲区。没应答就会继续重发,造成粘包的原因有几种。
    1. 发送端的多个数据包在缓冲区里一起发送
    2. 接收端由于网络原因阻塞,然后一次性接受多个包
    另外数据包大小比缓冲区大小大的话,会被分成多个片分开发送。
    解决以上问题,可以在包的结构设计上处理,一般都采用 包头(4字节)+数据 的结构来封包,包头写入包的长度,下面请看解析包代码
    /**
     * 封包
     * **/
    private void send(Message vo){
        if(vo == null)return;
        MemoryStream buff = new MemoryStream();
        byte[] voByte = Message.encode(vo);
        byte[] pLen = ByteArrayUtil.writeInt(voByte.Length);
        buff.Write(pLen, 0, 4);//写包头
        buff.Write(voByte, 0, voByte.Length);
        _socket.Send(buff.ToArray());
    }
    //开一个子线程接受数据
    class SocketThread
    {
        public Connection conn;
    
        private int len = 0;
        public void run(){
            while(true){
                if(conn.state != Connection.CONNECTED)continue;
                if(len == 0){
                    if(conn._socket.Available < 4)continue;//不足4字节读包长
                    byte[] lenB = new byte[4];
                    conn._socket.Receive(lenB, 4, SocketFlags.None);
                    len = ByteArrayUtil.readInt(lenB);//读出包长
                }
                if(conn._socket.Available < len) continue;//不足一个包
    
                byte[] voB = new byte[len];
                conn._socket.Receive(voB, len, SocketFlags.None);//把包读出来
    
                len = 0;
    
                MemoryStream ms = new MemoryStream(voB);
                Message vo = Message.decode(ms);//解码包
                ms.Close();
                Debug.Log("receive:"+vo.getClassName());
                conn.resultConnectionHandler(vo);
            }
        }
    }

    2.协议加密校验

    很多游戏的数据都是明文传输的,别人使用一些抓包工具如Wireshark、WPE等等,就可以轻易修改网络包数据。为了防止数据被修改,我们可以做一下校验,这里介绍一种简单的加key校验方法,服务端跟客户端都用同一种校验算法,根据key生成一个校验值,然后在数据中把这个校验值传输给前端,前端再做校验判断
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace test
    {
        class Program
        {
    
            private byte[] datas = new byte[100];
    
            public Program() {
    
                //随机生成100个字节的测试数据
                Random random = new Random();
                for (int i = 0; i < datas.Length; i++)
                {
                    datas[i] = (byte)random.Next(1, 255);
                }
    
                ArrayToString(datas);//打印数组
    
                //校验密钥
                int key = 109;
    
                int checkVal = CheckBytes(datas, key);//生成校验值
                Console.WriteLine("校验值:" + checkVal);
    
                datas[3] = 2;//模拟抓包工具修改数值
    
                Console.WriteLine("模拟抓包工具修改数值:");
    
                ArrayToString(datas);//打印数组
    
                int checkVal2 = CheckBytes(datas, key);//再次生成校验值,此时数据已经在上面被模拟修改了
                Console.WriteLine("校验值:" + checkVal2);
    
                if (checkVal != checkVal2)
                {
                    Console.WriteLine("检测到被修改的数据包!");
                }
    
            }
    
            ///
         /// <summary>
            /// 根据key校验字节数组
            /// </summary>
    
            public int CheckBytes(byte[] datas, int key)
            {
                int sumCalc = 0;
                int i = datas.Length;
                //每一个字节都会影响校验值
                while(--i > -1)
                {
                    sumCalc += (int)(datas[i] ^ key >> datas[i]);
                }
                return sumCalc;
            }
    
            public void ArrayToString(byte[] datas)
            {
                string str = "";
                for (int i = 0; i < datas.Length; i++)
                {
                    str += datas[i] + ",";
                }
                Console.WriteLine(str.Substring(0, str.Length-1));
            }
    
            static void Main(string[] args)
            {
                new Program();
    
                Console.ReadKey();
            }
    
        }
    }

    运行结果:

    不过如果你的客户端代码被别人反编译了,别人知道了怎么获取key以及校验算法后,就能把值修改成能校验通过的数据,所以怎么隐藏key很重要,客户端代码加密混淆也很重要。

  • 相关阅读:
    Linux sed命令实例详解
    hadoop2.0 和1.0的区别
    linux如何修改主机名
    hadoop主节点(NameNode)备份策略以及恢复方法
    Hadoop 添加删除数据节点(datanode)
    Hadoop常见错误及处理方法
    【转】ImageView.ScaleType属性
    MonoBehaviour.print和Debug.Log是同样的作用
    unity自带寻路Navmesh入门教程
    前向渲染路径细节 Forward Rendering Path Details
  • 原文地址:https://www.cnblogs.com/lijiajia/p/6861282.html
Copyright © 2011-2022 走看看