看到这个服务端框架,是真的轻量,代码极简但包含了服务端的核心,能很容易帮助新手理解服务端框架做了什么。
当然最主要还是作者写的渐进式开发文档,实在是极好。
这篇文章不会详细的说这个项目的源码,这个直接看作者的文档和源码就行,这篇文章主要是借助这个框架总结一个tcp服务端的核心功能。
一个TCP服务端要给客户端提供功能,首先得与客户端建立连接。
学过网络编程都知道,任何教材的第1步都是教你使用socket bind listen connect等这几个函数。
当然这只是最基础的系统调用,作为一个框架,自然要把这些功能封装好,统一处理连接和管理不同的客户端。
zinx 已经实现了这个功能。九、Zinx的链接管理
在作者的教程文档中,这篇已经很靠后了,因为作者是一行一行代码,逐步这个框架上挺增加东西的。这也是我觉得这个作者写的很好的原因,能让新手知道最初是怎么做的,然后为什么要加之后的这些功能。
连接建立之后就是处理消息。
一句话来说,作为一个服务端的程序,收到客户端的消息,作出相应的处理,然后如果需要响应的话再返回给客户端响应消息。
事儿是这么个事儿,但具体怎么识别消息,怎么处理消息需要做一些规定,这就是协议。
比如HTTP就是一种应用层协议,规定了Web客户端和外部服务器应该怎么交互?服务器返回200就是OK返回404,就是没找到页面。这个不是本来如此,这只是一种规定。
自己选的TCP服务,也可以使用HTTP协议,这是人家定义好的。但是没有必要。HTTP协议里面有太多的冗余之外,为增大网络流量。
这时候我们就可以自定义一种精简的协议。比如最简单的也是很常见的。把整个消息分为消息头和消息体,固定消息头的长度,其中的第1个字节表示消息体的长度,第2个字节表示这个消息类型,剩下的是消息体。
这就是我们定义的协议。
很多教材都会在这里说粘包问题,我觉得这是一个伪问题。又是一个不是问题的问题。只要协议定义好了。就没有这个问题。但如果不定义协议。就不只是粘包的问题。反正不需要关心这个问题。
协议定义好之后,我们服务端就知道怎么解析网络消息了。
比如来了一条消息,我先提取消息头长度的数据,知道了这条消息的消息体有多长,再把消息体读出来。
这时候读出来的数据都是二进制的,把这个二进制的数据变成我们程序能认识的数据,就是反序列化。
如果要序列化与反序列化,就要先固定原消息的消息格式,现在比较常见的,就是json和protobuf,两者具体是什么就不赘述了,在游戏的网络通信中,还是pb比较常见一些。
拿到二进制数据之后就可以调用Pb工具来反序列化这些数据,变成我们定能理解的代码结构(类、对象)。
之后就是根据自己的业务逻辑进行处理,处理完之后,再次使用这个连接,序列化处理结果,然后发送出去。
路由就是识别收到的消息,具体是什么消息?这是业务层面的一个概念。比如对于游戏服务器来说,区分这是一条登录消息?还是一条退出消息?收到不同的消息要怎么处理?
简单来说就是一个switch的结构。发的消息中带有这个消息类型。然后我switch消息类型。不同的case做不同的处理。稍微有点编码经验的人都知道。这样就是不利于拓展啦,怎么不利于协作呀之类的。需要把case抽象出来。就给他定义成路由。
一个路由处理特定的消息。比如我写一个登录消息的路由处理。再写一个退出消息的路由处理。所有路由存在一个map里。
我收到消息的时候,根据消息类型就取出不同路由来处理。这是框架做的事情,业务开发者只需要去写不同的路由就行了。具体可以看这个框架的源码和作者的文档。六、Zinx的多路由模式
至于其他的功能都是一些拓展或者是未来提高服务端性能而设计的。
比如这个框架中用到的worker模式和连接属性。
worker实际上就是一种协程池。
连接属性是对这条连接的一些补充信息。如果作为一个游戏服务器,客户端会与服务器建立一条TCP长连接。服务器可以给这条连接上,标识一个有ID也就是用户ID,来识别这条连接,是属于这个用户的。
这有什么用呢?比如玩家掉线了这条连接就断开了,然后服务端就可以通过这个这个链接知道是哪个用户掉线了,再做一些掉线处理。
想了想一个TCP服务端框架,最核心的功能也就是对连接的处理和对消息的处理,其他暂时想不起来了,以后想到再补充吧。