在Unity框架设计中与游戏服务器对接的网络框架也是非常重要的一个模块,本文給大家分享如何来基于Unity来设计一个网络框架, 主要的讲解以下几个点:
这里有个游戏开发交流小组 大家可以一起学习交流
(1) TCP半包粘包, 长连接与短连接, IO阻塞;
(2) Tcp Socket与UDP Socket 的技术方案;
(3) Unity的序列化与反序列化技术方案;
(4) TCP的封包与拆包;
(5) 基于http的短连接技术方案;
(6) Unity 网络框架设计与实现原理;
TCP 是可靠的网络传送协议,网络传输底层每发送一个TCP数据包,就要等对方确认,收到确认消息以后才能发下一个TCP数据包。当我们在应用层发送一个应用层的数据包的时候,TCP网络底层可能会把这个应用层的数据包分成若干”TCP数据包”,通过网络底层发出去。那么这里就会有一个问题,应用层发的数据包有可能被拆散成几个”TCP数据包”发出去,我们在另外一端接收的时候,可能要把这些拆散的包组合起来。这个就是”半包”。底层有可能把两个应用层的数据包分到一个”TCP数据包”发过去,接收到后,一部分是属于上一个应用层的数据包,一部分属于下一个应用层的数据包,这个叫做”粘包”。
TCP 是面向连接的,服务端与客户端通过TCP 连接来传送数据,如果连接一直在,发送完数据后,不关闭连接,下一次发数据可以直接发送,我们叫做长连接。如果发送数据先建立连接,数据发送完毕后,马上关闭连接,下次要发送数据重新建立连接,这种叫做”短连接”。长连接一直存在,优点就是客户端与服务端随时可以相互通讯,但是一直占用连接资源。短连接是不占用连接资源,但是只有客户端能像服务端发送数据,服务端在断开连接后无法主动通知客户端。
IO阻塞指的是我们的CPU处理数据的速度,远远大于网络的传送速度。当我们要发送数据的时候,CPU调用IO的接口,把数据从内存拷贝到网络底层,等待底层把数据传送过去后,CPU调用的IO函数返回,而这个过程,CPU在等待网络把数据发出去,线程挂起了,这个我们叫做IO阻塞。
了解完网络的一些基本概念以后,接下来看下使用TCP/UDP Socket用哪些技术方案。Unity其实是作为网络的客户端,而客户端只要去连接服务端与服务器通讯就可以了,不用像服户端同时处理N个客户端的数据传送。所以客户端Socket非常的简单。Unity客户端的Socket我们用哪个插件呢?其实这种完全不需要用什么插件,直接使用OS为我们提供的Socket的相关API就可以了。
TCP Socket 面向连接的,使用流程如下:
1: 客户端Connect服务端,建立Socket连接;
2: 调用Socket的Send函数发送数据;
3: 调用Sokcet Recv函数从Socket上读取数据;
4: 关闭Socket 的连接;
UDP Socket的使用, UDP Socket不是面向连接的,只是调用底层的网络协议,直接把数据包发往特定地址+端口。所以直接是SendTo(网络地址+端口), RecvFrom(网络地址+端口)
TCP/UDP Socket已经足够简单,Unity开发者在选取技术的时候直接使用即可。
网络发送的都是数据字节流,Unity与服务端通讯要把要发送的数据对象变成二进制字节流,然后通过网络传送出去,收到字节流以后,又要重建回数据对象,完成数据发送。数据对象变成二进制字节流这个过程叫序列化,把二进制字节流转回数据对象叫反序列化。
序列化/反序列化目前主要有两个打的方向:一个是二进制序列化,一个是文本序列化;
二进制序列化/反序列化:基于二进制的bit,通过用户商量的协议来把数据对象变成二进制bit数据流,反序列化的时候根据用户协议,来把bit数据流变成数据对象。
文本序列化: 将数据变成人眼可读的文本数据。当收到序列化好的文本数据的时候,根据文本数据的规则来解析出里面的数据重建数据对象。
二进制序列化/反序列化: 我们把一些基本的数据类型用bit来进行编码,如 int, float, string bool等。写好这些基本数据类型的编码和解码的代码, 然后编写要数据对象的协议,然后开发一个编译器,他可以根据这个协议文件,基于基本数据类型的编码/解码函数,根据协议,结合基本数据类型的编码解码,按照对象的结构,拆分成若干基本数据类型,自动生成编码/解码函数代码。
文本序列化的解决方案: json, xml等;
二进制序列化/反序列化解决方案: protobuf;
上面讲解了,应用层的数据包有可能被拆分成若干”TCP数据包”,还有可能两个应用层的数据包连在一起在一个”TCP数据包”中。为了收数据的时候能完整正确的收到应用层的数据包,我们必须要把两个应用层的数据报之间做好分隔标记,当我们收到数据以后就可以根据分隔标记来决定哪些数据是哪个包的。目前有两种做法:
(1) 大小+数据内容+校验码模式;[size][数据body][校验], 游戏开发中经常会使用这种方式来做封包, 收数据的时候,根据大小来将数据包完整的组合出来。WebSocket也是采用的这种方式。
(2) 特定的分割符模式: 123456\r\n67890, Html采用特定的分割符号来拆分数据内容。
短连接,前面个讲过的,用完就断开连接,下次要用的时候再重新的连接。短连接是使用TCP Socket策略,而这种策略最常用的就是http(基于TCP Socket+http超文本传输协议)。
Unity网络框架中也要使用http,主要是和服务器对接基于http通讯以及资源服务器的下载等需求。Unity作为客户端,要使用http,都已经封装好了UnityWebRequest,同时UnityWebRequest是多线程模式,可以同时并发N个请求。
铺垫了这么多,我们直接来上Unity网络框架的架构设计结构图,供大家参考。
这里接收数据的时候,采用多线程来做,收到数据后,进行拆包与反序列化,得到数据对象后,放事件队列,游戏主线程从事件队列里面获取网络事件,然后进行处理。发送数据出去的时候,我们在游戏主线程调用异步的网络Send函数,避免IO阻塞。注意好这些点,就可以设计出一个很好的网络框架。
本节分享就到这里了,关注我,学习更多的Unity框架设计的技巧。