Go游戏服务器防外挂之-安全通信算法

游戏服务器防外挂-通信安全 在游戏行业有太多因为外挂程序最终导致游戏产品快速死亡的血淋淋的教训了。这些外挂程序就是利用篡改/模拟客户端/服务器之间的通信数据来进行作弊牟得额外收益。因此在网络游戏中客户端/服务器之间的通信安全是游戏服务器技术中至关重要的一环。我们这篇文章讲一讲关于如何使用加密/校验的方式进行安全通信。 外挂利用非安全通信作弊溯源! 很多时候程序员修复bug/漏洞和医生治病差不多都要对症下药。一个可以被外挂作弊的服务器程序提供的服务可以说是一个病态的服务,那么为了解决这个问题我们需要找到症结所在。 我们先从对于客户端/服务器通信数据不采用任何安全处理方式开始分析。二者之间的通信数据虽然被转换成了二进制字节流的方式进行通信,但是以目前的技术手段这些数据几乎可以理解为就是明文传输的。那么外挂程序可以利用多种手段对通信数据进行二次加工。 通过修改客户端内存的方式篡改通信数据 通过拦截客户端/服务器收到/发出的协议包进行二次篡改 通过其他脚本/程序模拟客户端和服务器进行通信 ...... ...
Read More

Go游戏服务器热更新

Go游戏服务器热更新 1.为什么需要做代码热更新? 什么是代码热更新 所谓代码热更一般是指服务程序在不停止服务运行的情况下动态替换其中部分代码对原有逻辑进行修正或者新增功能代码。 为什么需要代码热更新 服务程序一般需要为应用程序提供长时间724小时不间断的服务。 不停止服务程序的运行的情况下动态修改程序的行为逻辑。 游戏服务器频繁停服维护非常不友好。* 2.游戏服务器热更主流解决方案? 代码热更的基本原理 我们可以用一个公式来简单概括服务程序。服务程序=数据结构(数据管理)+算法(这里说的算法指代程序中对于数据的计算处理相关的部分)。大部分时候代码热更新只能热更算法的部分,数据部分是非常难热更的或者说不建议热更的。举个例子: [crayon-6745df3e43b2b468728878/] 上述代码可以进行热更的部分是ChangeGameData方法。试想一下如果修改了GameData数据结构,删除了D字段新增了E字段,那么此时数据结构与程序内存中的GameData对象的内存结构是不匹配的,那么对于字段的操作也不能达到预想的效果。到这里我们热更的目标很明确了,就是如何能够动态的替换掉ChangeGameData这类的对于数据的计算的函数? 代码热更方案: 基本原理 动态库替换 编译型语言+(运行时编译器(luajit/cpython)+动态解析内嵌脚本) 实践方案 c/c++动态库(.so或者.dll)替换 c/c++内嵌lua c/c++内嵌python node.js golang+plugin 3.Go游戏服务器如何做热更新 前面实践方案中已经剧透了Go语言中实现代码热更新的方式是采用plugin包加载动态库的方式。go编译器支持将一个go程序编译成一个插件(plugin)。plugin的基本原理类似于c/c++加载动态库的方式。 插件程序构建 程序目录结构 [crayon-6745df3e43b30476722074/] 代码解析-插件代码(plugintest/plugin/main.go) [crayon-6745df3e43b32628660700/] 代码解析-宿主程序代码(plugintest/main.go) [crayon-6745df3e43b33043693728/] 编译-plugin编译 ...
Read More

如何为分布式游戏服务器中的实体创建全局唯一数字id

游戏中需要使用唯一ID的场景 游戏场景唯一id 场景中的实体的唯一id(NPC、Monster、Building、Resource...) 业务系统中的唯一实体的id 唯一ID的实现方式 字符串唯一ID 数字唯一ID 如何设计一个全局唯一的数字id 使用数字做全局唯一id面临的问题 最大使用uint64位整数做唯一id 如何是分布式系统中各个服务(程序)创建全局唯一id 如何尽可能的使唯一id生成器使用足够长的时间 uint64位整数如何做到全局唯一? 要想在分布式系统做做到唯一id,那么意味着这个唯一id中已经包含/表达了足够区分各个系统(服务)的信息,那么如何在一个只有64bits的数字中包含这些信息呢? 第一步考虑就是需要在id中包含一个机器码(服务的唯一id)以此来区分不同的服务实例. 如何保证高并发环境下各个服务实例同时创建的id的唯一性. 如何保证同一个实例高并发环境下唯一id的生成. 基于Sonyflake改造后的idgenerator 想要了解如何使用一个数字去做到全局唯一首先需要了解一些雪花算法Snowflake,这是Twitter公司提出来的算法。因为Snowflake的灵活性和缺点,比如索尼的Sonyflake等等。我们这里也是基于这个算法进行改造。 我们先看下原生的Snowflake算法,原生Snowflake算法使用一个64 bit的整型数据,根据当前的时间来生成ID。 原生Snowflake结构如下: 因为最高位是标识位,为1表示为负数,所以最高位不使用。 41bit 保存时间戳,精确到毫秒。也就是说最大可使用的年限是69年。 10bit 的机器位,能部属在1024台机器节点来生成ID。 12bit 的序列号,一毫秒最大生成唯一ID的数量为4096个。 Sonyflake是基于上面的snowflake改造的,变更了时间、机器码、序列号的位数,snoyflake的结构图如下: 下面来看看我们基于Sonyflake改造后设计实现的idgenerator的基本结构说明: 36bit 保存以10ms为时间单位的时间戳,最大可使用的年限是21年。 13bit 的机器位,能部属8192个节点来生成ID。 14bit 的序列号,10毫秒最大生成唯一ID的数量为16384个。 根据上面的结构我们可以看出来我们调整了时间、机器码、序列号的位数,下面我们来分析一下这么调整的原因。 时间位36bit 首先我们看下36bit能够表达的数最大值是1...
Read More

golang-plugin模式代码热更之-可恶的protobuf重名注册panic问题

  如果你在使用golang的plugin模式进行代码热更新时会发现protobuf编译生成的xxxxx.pb.go文件中会生成2个init方法。这2个init方法会向"github.com/golang/protobuf/proto"包的全局变量进行注册,原本这种注册在常规的golang程序中是没有任何问题的,因为一个package的init方法只会调用一次。但是在使用plugin模式时,如果xxxxx.pb.go文件是在plugin插件代码中,那么每次新插件加载的时候都会调用对应包的init方法,并且因为需要热更新plugin,所以就会进行多次调用了。因为在"github.com/golang/protobuf/proto"包中多次进行注册同名的类型名会触发panic,因此起初我要解决的问题是如何可以避免这个panic? 我们先看看触发panic的代码片段:[crayon-6745df3e440e0218465148/]  每次golang宿主程序加载新的插件hotswap_myplugin.so(该插件包含error.pb.go代码文件)都会调用上面的init方法中的RegisterEnum进行枚举类型的注册触发panic。为了解决这个问题我想到2个思路:  1. 每次新的插件变更error.pb.go中的变量的命名  2. 去除init方法中的注册代码  根据实际应用场景的分析,第一种方法不太适用,因为每次都变更error.pb.go的命名的话意味着插件中的其他代码引用error.pb.go中的类型或者变量的代码都需要全部修改,这对于需要经常热更的代码场景来说显得比较麻烦,一个原因是类似error.pb.go的文件会非常多,每次所有的proto文件都需要修改,另一个原因是所有引用代码也需要全盘修改。这个方式显得非常鸡肋,因为大多数时候如果我只想修改一行代码去修复某个bug,却要花费大量的时间代价去重命名proto定义文件去避免重名注册panic。  那么如此只好使用第二种方式了,第二种方式是希望能够不要执行init方法中的注册方法那么就不会触发重复注册panic了。好家伙,这意思是要从源头解决问题了,no...
Read More

[译]-Go-FAQ

[TOC] FQA 起源 开启这个项目的目的是什么? 10年前Golang项目开启之时,当时的编程世界和现在是截然不同的。软件的制作通常都使用C++或者java来进行开发,那时还没有github,大多数电脑还不是多核处理器,和Visual Studio和Eclipse相比没有更高级的IDE(集成开发环境),在互联网上免费开发使用的就更不用说了. 与此同时,让我们非常懊恼的是我们需要使用我们常用的开发语言进行非常复杂的服务器软件的开发.计算机的速度已经变得非常快了,但是我们最初使用的诸如C/C++、java之类的编程语言本身并没有很大的进步. 因此,显而易见的的是计算机的多处理器会逐渐变成常态,但是编程语言在如何高效、安全的运用多处理器方面只提供了很少的支持. 我们决定退一步思考随着科技的发展在未来若干年哪些问题才是主导软件工程关键因素,并且一门新的语言该怎样帮助解决这些问题.例如,随着多核CPU的崛起争论最多的是编程语言首要应该提供并发或者并行的执行顺序方面的支持.让高并发的程序的资源管理变得更加优良,提供垃圾回收机制,至少提供一些安全的自动管理内存的机制. 这些方面的思考引出了一系列的讨论,这也直接导致了Golang的诞生,第一步是一组想法和期望,最后衍生出了一门编程语言. Go的总体目标是可以通过启用工具完成自动格式化代码的任务以及消除在大型代码库上工作的困难方面给程序员提供更多的帮助. 关于如何实现这些目标或者至少实现这些目标更多的描述在这篇文章中有更详细的描述. Go at Google: Language Design...
Read More

The Go Memory Model

The Go Memory Order      使用过Go语言或者听说过Go的都知道Go号称天生在并发编程上具有先天优势,其实会让人好奇他的先天优势来源哪里呢? 按照官方解释或者目前大多数人的认识来说呢应该是Go语言层面提供了一个并发执行体Goroutine,很多人把这个叫go协程,原因是因为它的创建执行消耗的计算资源非常轻量,和协程coroutine长得很像。但是在了解了Go的调度模型后,我个人感觉Goroutine的运行模式本质上更像是线程池的模型。当然这篇文章要写的主要是Go的内存模型,但是内存模型和Go的并发密切相关。因为讲到并发,很多人的第一反应就是并发安全问题,讲到并发安全就离不开数据同步,所以这篇文章的目的是根据Go官方提供的一篇文章 The Go Memory...
Read More

Unity Hub 服务器无响应解决方案

     本人游戏服务器开发一名,最近尝试学习一下Unity开发,Unity Learn的几个项目要求Unity版本2019.1。2019版本的Unity需要安装Unity Hub。然后我到官网下载好了Unity Hub安装之后发现始终登录不上我的Unity账号。一直报错提示服务器无响应。根据直觉应该是网络不通,被G-F-W墙了? 后来尝试使用shadowsocks全局模式再登录发现依然无法登录(大部分人到这里应该已经可以登录了),我这里无法登录是有其他原因的(后来发现是被自己公司墙了)。   如果遇到和我一样的情况,可以尝试下面的解决方法。第一步如果你有可以使用VPN那么后续的就不用看了,VPN连上去应该不会有无法登录的问题了。如果没有VPN而是使用的类似ss的工具,那么需要设置环境变量,使得Unity Hub能够使用ss代理。根据我测试的情况来看,Unity Hub没有默认使用系统代理,所以ss开启系统代理是无效的。 ...
Read More
12