
SERVICE PHONE
363050.com发布时间:2025-07-01 03:50:46 点击量:
多宝体育,多宝体育官方网站,多宝体育官网,多宝登录,多宝体育下载,多宝体育网页登录,多宝体育app,多宝体育试玩,多宝体育入口,多宝体育注册网址,多宝体育登录,多宝官网,多宝网址,多宝注册,多宝电竞
369棋牌游戏的总体设计思路是先设计了一个游戏框架作为游戏内核,向外暴露出接口,供游戏逻辑部分调用。
服务器端框架这部分编译好以后是一个kernelEngine.dll。这个dll向外暴露出几个Service接口。游戏的服务器端启动时,主要就是调用这几个Service接口的start()方法。先大致列举一下有哪几个Service,如下图所示:
下面那个队列服务,是不对外暴露的。它是供上面那几个引擎(也即Service)做请求排队用的。因为一个引擎,比如网络引擎,可能提交给它处理的请求同时有多个,这样就需要先放入一个队列里面。
这个队列本身是做成一个服务的,启动该服务,会跑一个线程。该线程就是一直查询队列里的数据,发现有请求数据就调用服务的一个方法。而服务的这个方法会调用一个回调接口的方法。引擎如果要用到队列服务,就实现这个回调接口,然后初始化时把自己的指针传给队列服务。
1.因为要处理多个排队请求,它实现了队列服务的回调接口,把自己的指针传给了一个“发送队列服务”对象。
2.建立了一个IO完成端口。(IO完成端口主要就是windowsAPI的一个机制,其他平台估计也有类似机制。它主要就是把一个线程和IO操作状态关联起来。当一个IO操作完成时,windows系统把一个操作完成包放到一个队列里,和此IO完成端口关联的线程会调用一个windowsAPI,读取该队列的操作完成包,拿到该包后,就可以根据包数据作些操作。如果队列里没有完成包,线程会挂在那里等待。)
3.这里建立的IO完成端口依次和ServerSocket也就是监听套接字,Accept进来的每一个通信Socket绑定。这样每次通过这个Socket读入数据还是写出数据,都能接收到IO完成端口的通知。以上这个绑定操作,是在一个名叫CsocketAcceptThread的“接收”线程里做的。现在网络引擎启动时,把这个线.作为IO完成端口队列的消费者,还需要一个线程去拿到队列里的完成通知包。然后根据通知包,做一些操作。于是引擎也启动了这个线程。这个线程内的,它检测到这是一个读操作完成,就调用网络引擎的一个OnSocketReadEvent方法。这个SocketReadEvent方法会把一个EVENT_SOCKET_READ预定义的常数操作码,和数据buffer添加到调度引擎的操作队列里(就是用那个队列服务实现)。调度引擎里有一个switch…case结构,区分了几大类操作。其中有一类就是EVENT_SOCKET_READ(其他还有数据库操作的标识)。这个switch…case正好是放在调度引擎的队列服务的回调接口里的,供队列服务的线程调用。
网络引擎还有SendData方法,这主要就是把操作请求加到“发送”队列服务里。队列服务再调用引擎的回调方法。回调方法里就是用Accept来的保存起来的Socket发送数据。
调度引擎该引擎就是接收其他引擎加到它的队列服务里来的操作请求数据,并进行处理。处理时呢,又向外面暴露出一个回调接口(钩子)。这个回调接口不由调度引擎自己实现,靠启动引擎时,由包装引擎的应用程序,比如游戏服务模块,传入一个实现。
游戏服务模块的这个实现就是CattemperEngineSink这个类。类里的这个OnEventTCPNetworkRead方法被调度引擎调用。这个方法也是一个switch…Case结构。该结构内有这么一段:
用来处理游戏客户端发来的消息。在OnSocketMainGame方法内部,又把处理委托给TableFrame类。该类主要管理具体一点的游戏操作:比如玩家坐下,起立,启动游戏,每一局结束写分等等。撇开这些不谈,涉及到和客户端通信:发送是调用的CattemperEngineSink里的发送方法。而处理客户端发来的消息,是委托给一个回调接口的。该回调接口由具体游戏比如:牛牛来实现,名字叫CtableFrameSink的这个类。
这个类的OnGameMessage用了一个switch…case结构处理各种客户端发来的游戏消息。根据各类消息,结合GameLogic类进行处理。
客户端框架客户端的网络通信也有一个框架。该框架相对简单,连接是在connect()方法里作的。连接后把socket句柄保存在成员变量里。然后建立了一个隐形的窗口,把socket和这个窗口关联起来。
用到了一个windowsAPI:WSAAsyncSelect。该API使得每当Socket的状态发生变化时,就会发送一个自定义的窗口消息给那个隐形的关联窗口。根据windows的消息循环机制,由窗口关联的过程来处理这个消息--又是一个switch…case结构,对消息进行了分类:连接时,读取时,关闭时。分别处理。在处理时,也不是框架自己处理的,是委托给了回调接口。回调接口的实现由具体游戏提供(比如牛牛)。
回调接口一般用于处理接收到的数据,发送数据直接通过保存的socket句柄发送。
一个发送数据的函数,一般都包括有至少这四个参数:一级父消息(常数),二级子消息(常数),一个数据指针(指向一块缓存:BYTE*或者void*),数据缓存的字节数。“发送时”构造一个结构体(struct),结构体起码有4个域对应这四个参数。其中,对应缓存的参数,也就是数据指针,要把它指向的数据拷贝到结构体的对应域中。其它各个域一一赋值。
这样的消息头和消息体放在一个结构体中的安排是用于服务端框架内部分层结构之间调用传递的;到最终要向客户端发送(使用Socket)时,情况有些不同:读取出这个结构体的消息头(包含有父消息标识和子消息标识),构成一个独立的CMD_Head结构,然后再利用C内置的指针操作:(CMD_Head*)p,…,p++,这两步,先把分配好内存,要用来发送的数据缓存区指针p,强制类型转换为(CMD_Head*)指针类型,方便对其父子消息域赋值,然后p自增1,p就指向了消息头尾部,也就是消息体头部,然后用内存拷贝API,向p指向内存区域拷贝消息体。构造好了缓存就可以用WINSOCKAPI向远程客户端发送数据了。
接收的时候,由于WinSockAPI得到的是一个缓存区,就用(CMD_Head*)p强制转型,然后就读出CMD_Head结构体的域,作相应处理。
这里要说明一下的是,前面为了说清楚大致原理轮廓,忽略了细节:其实CMD_Head结构体并非直接包含父子这两级消息头的,它涉及的实际定义如下:(包括一些相关联结构体)
CMD_Info这个结构体主要是用于效验数据的,其中的cbCheckCode域是解密消息体的时候,按照一定的算法来验证的。
(当然服务端在发送前,显然是用相对应的算法加密消息体,同时得出这个cbCheckCode域,然后填充在结构体里的。)
CMD_Command这个结构体才是放两级消息头的。然后根据这两级消息switch…case。
注:父消息和子消息只是#define 定义的符号常数,分为父子两级只是为了便于游戏内部消息分类。