关于后端:网狐核心源码阅读分析

4次阅读

共计 12746 个字符,预计需要花费 32 分钟才能阅读完成。

框架结构

网狐服务器整体上分为 4 个局部:直达服务器,房间服务器,大厅服务器,sqlserver 数据库。其中大厅服务器次要负责帐号管理器:治理用户抉择服务器登录地址,校验用户数据等。必须与直达服务器放弃长连贯,用于更新获取最新数据。
房间服务器:用于加载解决每款子游戏逻辑与公共游戏逻辑(例如机器人整体随机进出任何游戏房间,机器人游戏信息处理等。他也必须与直达服务器放弃长连贯,用于传输最新游戏房间信息给直达服务器。
直达服务器:收集所有房间服务和大厅服务器的 ip、端口、在线游戏人数等信息,并负责直达全局的游戏音讯,所有房间服务器大厅服务器都在线连贯直达服务器,定时更新状态。直达服务器通过连贯平台库,获取平台的信息。
同时这三个服务器又别离和数据库进行连贯, 能够独立进行操作对应的数据库。这三个服务器都是依赖于爱玩内核而设计, 能够看做是基于爱玩内核对不同业务需要的不同实现。

框架结构图

大厅服务器

启动流程:

  1. 点击启动服务按钮调用 OnBnClickedStartService()函数,调用 StartService()函数进行初始化
  2. 配置信息:在 StartService()函数里先加载配置信息, 配置信息包含初始化用户数据库,财产数据库,站点页面, 过程以后启动目录,相干配置文件门路名称,获取本地 ip,直达服务器信息等,而后配置数据库信息,设置最大连接数为 2048,监听端口,udp 监听端口。
  1. 创立必要组件:m_TimerEngine 定时器引擎(用于做脉冲心跳定时获取与核心服务器的最新列表信息)
    m_DataBaseEngine 数据库引擎(解决业务层投递信息,解决写入数据库)
    m_AttemperEngine 调度引擎(用于解决业务)

m_TCPNetworkEngine 网络引擎服务
m_TCPSocketTransfer 外部网络服务

  1. 创立数据库引擎和调度引擎的回调钩子函数,并且和数据库引擎调度引擎的钩子函数成员变量绑定。这样绑定是为了引擎能调用钩子函数。
  2. 把调度引擎顺次和工夫引擎,外部网络服务,网络引擎外部的事件接口成员变量绑定,这样绑定是为了其余引擎解决完本人的工作后能把工作投递到调度引擎。
  3. 把网络引擎和调度引擎外部的网络接口成员变量绑定,这样做惟一目标是当调度引擎解决网络引擎投递过去的工作失败时,能敞开对应的 socket 句柄。
  4. 把调度引擎回调钩子函数的配置参数,事件引擎,数据库引擎,网络引擎,外部网络服务顺次初始化为下面创立的组件。这样绑定后重写的调度回调钩子函数就能调用内核引擎来解决工作。
  5. 始化数据库回调钩子函数的配置参数,事件接口。这样数据库引擎操作完数据库后,能够把后果投递给调度引擎。
  6. 络引擎的最大连接数,监听端口,设置好工作目录。读取内核设置,查看并创立内核日志、大厅日志目录,而后顺次启动工夫引擎,外部网络服务,数据库引擎,调度引擎,网络引擎,UDP 引擎。
    大厅服务器次要性能:

    次要性能包含:

  7. 解决玩家注册,登录大厅过程中获取一系列包含,用户材料、头像、银行信息、账号密码,工作处分,零碎赠送,用户成就和是否代理,是否绑定推广,等辅助信息系统赠送等等所有用户在咱们游戏中的个人信息
  8. 定时查看与直达服务器的长连贯信息,每隔 30s 向直达服务器发送一次 SUB_CS_GET_SERVER_LIST 音讯用于更新列表信息,包含游戏类型信息,游戏品种信息,房间信息,如果开启网关,还有网关信息。

    定期更新的这些游戏服务器信息,会在玩家登陆胜利后发送给客户端

3.解决创立桌子流程
4.解决创立俱乐部,查找俱乐部,删除俱乐部,复制俱乐部,批改俱乐部,降级俱乐部,增加俱乐部成员等等与俱乐部相干性能
5.解决玩家玩家批改个人资料,银行信息,实名认证,手机绑定相干性能
6.解决玩家游戏工作性能
7.解决用户成就性能
8.游戏商城性能

创立房间流程

玩家点击创立房间或者退出房间按钮,客户端给大厅服务器发送 MDM_GP_USER/SUB_GP_TABLE_QUERY_ONLINE 也就是用户信息 / 查问在线信息,服务执行 GSP_GP_QueryOnline 存储过程查问用户状况,查问后果返回给客户端,

客户端依据查问后果,从而让客户端判断玩家是走创立房间,退出房间还是走断线重连流程。
如果是创立房间流程, 会弹出创立房间界面, 玩家抉择好要开房的游戏后,发送 MDM_GP_USER/SUB_GP_TABLE_CREATE 用户信息 / 创立桌子信息给大厅服务器,大厅服务器发送 MDM_CS_PRIVATE_TABLE_MANAGE/SUB_CS_TABLE_CREATE 私人房桌子 / 创立桌子音讯给直达服务器
如果房间创立胜利,会收到来自直达服务器的 MDM_CS_PRIVATE_TABLE_MANAGE/
/SUB_CS_TABLE_CREATE_SUC 私人房桌子 / 创立胜利音讯。

而后大厅把这个音讯发送给客户端, 客户端坐下后会收到来自直达服务器的私人房桌子 / 刷新桌子信息音讯,同时大厅也会把这条音讯批量转发给客户端
综合以上流程能够看出:在直达服务器和游戏服务器没启动时,只有大厅服务器启动,玩家就能够登陆到大厅, 只是玩家点击创立房间时,因为没有直达服务器,所以玩家在发送创立房间信息时,音讯不能通过直达服务器转发给游戏服务器,所以也就创立失败,同时如果启动直达服务器,但不启动游戏服务器,当中转服务器收到大厅服务器的建房音讯后,会创立失败,而后返回创立失败音讯给大厅服务器,大厅服务器也会返回给客户端创立失败后果。

登陆大厅流程

依据客户端不同登陆申请,在 OnSocketMainLogon()函数里别离有账号登陆,微信登陆,注册账号,账号重连,微信重连这些不同解决流程

只看微信登陆这条流程

投递 DBR_LOGON_WX 数据库申请后执行 GSP_GP_LogonWX 存储过程进行参数校验,校验胜利后会获取 DBR_LogonSuccess 这个构造体所有用户相干信息,接着投递 DBR_LOGON_SUCCESS 这个申请,紧接着会依投递数据库申请取得辅助信息,签到信息,首充信息,明码信息,布告信息,会员信息,登录胜利后会发送 CMD_GP_LogonSuccess 这个构造体所有信息,和房间列表,播送音讯等给客户端

到此玩家登录大厅胜利

直达服务器

启动流程

从 CCenterService::StartService()这个函数启动,初始化网络设置,配置好 PlatformDB 平台数据库相干参数,创立数据库引擎,调度引擎,网络引擎,定时器引擎,创立好组件接口,并把这些组件和内核绑定,设置好数据库引擎和调度引擎的回调钩子,

配置网络引擎参数,包含设置好以服务端口为文件名的内核日志文件,设置好内核设置配置文件,建设内核日志目录。
而后顺次启动工夫引擎,网络引擎,数据库引擎,调度引擎。

启动网络引擎:会对实现端口进行一些初始化,并且启动异步引擎服务,配置好读写线程,应答线程,检测线程,并且启动这些线程,将服务设置为启动状态。
启动数据库引擎:线程启动函数 CServiceThread::ThreadFunction 执行,调用 OnEventThreadStrat(),OnEventThreadStrat()又调用 OnAsynchronismEngineStart()启动异步引擎,在异步引擎启动时会调用异步引擎钩子函数的启动函数 OnDataBaseEngineStart()。
启动调度引擎:

创立列表组件,并且设置好数据库信息,俱乐部数据库信息,加载列表,读取私人房、俱乐部配置,加载限度字符,

直达服务器次要性能

1.

每隔 60s 删除一次私人房,每隔 180s 更新一次私人房间配置, 每隔 5s 查看一次俱乐部主动开房
2.收到大厅服务器更新列表申请时,向大厅服务器返回品种列表,房间列表,网关列表,等信息
3.解决来自大厅的创立桌子,查找桌子,删除桌子,解决来自房间服务的玩家坐下,游戏开始,玩家起立,游戏完结,游戏扣钻等音讯
4.解决来自 web 服务的写分,充值,兑换,web 转账,流动处分,兑换房卡等音讯
5.解决来自大厅的俱乐部治理相干的申请
6.房间服务启动时,解决同步私人房信息,注册房间,同步人数等申请,房间服务敞开时,解决登记房间申请

创立房间到房间遣散敞开流程

玩家点击创立房间,直达服务器会收到来自大厅的 MDM_CS_PRIVATE_TABLE_MANAGE/SUB_CS_TABLE_CREATE 私人房桌子 / 创立桌子申请,

在对建房参数做一系列查看后,会给大厅发送 SUB_CS_TABLE_CREATE_SUC 房间创立胜利音讯,
接着直达服务器会收到来自游戏服务的 SUB_CS_USER_SIT_DOWN 玩家坐下音讯。

其余玩家输出房号退出房间后会收到来自大厅服务的 SUB_CS_TABLE_FIND 查找房号音讯,直达服务器会做参数查看,判断是否已在私人房,桌子是否存在,是否曾经开始游戏,是否人数已满,房卡是否足够等条件,如果所有查看通过, 会发送 SUB_CS_TABLE_FIND_SUC 查找房间胜利音讯,接着也会收到来自房间服务的玩家坐下音讯

房间遣散胜利后,会收到来自房间服务的所所有玩家的 SUB_CS_USER_STAND_UP 玩家起立音讯,和 SUB_CS_TABLE_ENDGAME 游戏完结音讯。

房间服务器

初步意识

通过查看整个服务器工程,发现房间服务源码文件是最多的,通过这些源码文件命名,和预览源码,能够看到房间服务器总体上能够分为: 机器人包含机器人治理模块,调度模块,因为调度模块性能较多,所以分为通用,AI,银行,直达服务,网关, 较量,移动用户,道具金币,工作,定时器这几个文件来写,还有数据库模块,启动模块,房间服务接口定义模块,局数工作模块,还有针对不同较量类型的功能模块,回放模块,性能测试模块,异步引擎,还有游戏桌子框架模块,发送 post,request 申请的 web 模块。

同时,房间服务还包含房间加载模块

启动流程

  1. 点击启动房间按钮后,OnBnClickedStart 函数会执行,初始化配置参数,能够看到房间服务会和 AccountsDB,PlatformDB,TGGameScoreDB 这 3 个数据库有分割,
    进入到 CGameService::StartService()函数,首先启动日志服务,接着别离创立工夫引擎,调度引擎,网络引擎,直达服务引擎,同步引擎,数据库引擎, 而后调整参数,给配置变量赋值,而后加载游戏服务模块组件,进入到 InitializeService 函数.
  2. 配置组件: 在配置组件时能够看到有大小为 5 的 CDataBaseSinkPrimary 和大小为 3 的 CDataBaseSinkAssistant2 个数据库引擎钩子数组. 其中 CDataBaseSinkPrimary 次要用来解决和玩家相干的数据库操作, 比方写分, 银行等. 因为这些操作须要保障程序性, 所以必须放在同一个解决数组外面. 而 CDataBaseSinkAssistant 次要用来解决不须要程序要求的零碎申请音讯.
  3. 绑定组件, 绑定组件的时候, 能够看到解决零碎申请的数据库操作引擎只有 1 个, 然而设置了 3 个数据库引擎钩子, 而解决玩家相干操作的数据库引擎有 5 个, 同时每个引擎都设置一个数据库引擎钩子.
  4. 而后就是把解决玩家申请的数据库引擎和引擎钩子和同步引擎绑定.
  5. 而后就是别离初始化 CDataBaseSinkPrimary 和 CDataBaseSinkAssistant 数组, 设置他们须要操作的数据库以及一些其余绑定, 留神到 CDataBaseSinkPrimary 绑定了同步引擎而
  6. 而后顺次启动工夫引擎,网络引擎,同步引擎,内核数据库引擎,记录数据库引擎,调度引擎和网络引擎。
    内核数据库引擎启动: 内核数据库引擎有 5 个同样的数据库引擎钩子, 所以会启动 5 个同样的线程, 在钩子函数的启动函数 CDataBaseSinkPrimary::OnDataBaseEngineStart()里会连贯用户数据库, 和提高数据库.

    记录数据库引擎启动: 记录数据库引擎有 3 个同样的数据库引擎钩子, 会启动 3 个同样的线程, 在钩子含税启动函数里会连贯用户数据库和金币数据库.

    内核数据库引擎和记录数据库引擎启动不同点:
    尽管都是连贯了用户数据库和金币数据库, 然而内核数据库是一共有 5 个数据库引擎, 每个数据库引擎一个钩子函数, 一个异步线程来解决工作, 也就有是说这 5 个数据库引擎期待在 5 个不同的实现端口上, 而记录数据库引擎是一个数据库引擎, 然而有 3 个异步线程, 这 3 个异步线程期待在同一个实现端口上.
    调度引擎启动:

    初始化变量,设置聊天,判断是银行领取还是,金币领取,如果是较量,会创立较量变量,创立玩家连贯信息变量,能够看到最大 520 个玩家用户,256 个机器人用户,调用子游戏服务中的 RectifyServiceOption 函数,在子游戏逻辑代码里对房间参数进行批改,

    而后创立并初始化所有游戏桌子,初始化机器人治理类,而后依据房间类型参数来对列表项形容构造进行不同初始化,而后连贯直达服务器,

    而后顺次设置了限度音讯,零碎音讯,心跳检测,加载配置,加载游戏工作等定时器
    读取 AI 配置,加载机器人,加载工作,设置加载机器人定时器,加载银行,桌子框架加载配置。

    房间服务次要性能

  7. 解决直达服务器创立完房间后来自客户端的坐下,旁观申请。
  8. 解决房间遣散或游戏完结后删除房间,玩家来到房间等流程
  9. 对所有桌子的治理,散发不同桌子的游戏音讯到对应桌子玩家。播送桌子音讯到同桌子其余玩家。
  10. 还有解决一些客户端和游戏服务器之间的处分,流动,等交互
  11. 玩家游戏完结后,解决对玩家写分,金币等数据库操作。
  12. 较量性能
  13. 定时更新整个房间服务的服务器信息,网关信息,私人房信息到直达服务器。
  14. 对机器人的治理
  15. 其余性能

内核引擎剖析

异步引擎

异步引擎工作流程实例

能够说整个内核能高效的进行网络传输和数据库操作,都是依赖于异步引擎,查看异步引擎头文件,有异步引擎类 CAsynchronismEngine 和异步引擎线程类 CAsynchronismThread,异步引擎线程类继承自服务线程 CServiceThread,在整个内核中有数据库引擎,调度引擎,网络引擎,这三个用到了异步引擎,通过大厅服务启动过程中网络引擎的启动,来阐明异步引擎的作用。

首先通过 m_TCPNetworkEngine->StartService()这个函数进入到网络引擎启动,先是创立了一个实现端口,容许机器外围个数到线程来运行,而后就是 socket 编程的绑定,监听操作,

接着通过 SetAsynchronismSink 函数创立指定个数的异步线程,并且通过 SetAsynchronismEngineSink 函数把异步线程的回调函数和启动异步引擎的引擎服务的钩子函数绑定或者引擎服务本身绑定。

网络引擎是创立了 1 个异步线程,并且绑定了这个异步线程的 m_pIAsynchronismEngineSink 回调接口为网络引擎本身。
而后执行 CAsynchronismEngine::StartService()启动异步引擎,

创立实现端口,并且之前创立了多少个异步线程,这个实现端口就容许几个线程运行,
并且把每个异步线程都和创立的实现端口绑定,而后就启动 StartThread 函数每个线程

通过 tagThreadParameter 构造体,把主线程指针,也就是该线程本人,传递到子线程到入口函数 ThreadFunction 中,同时通过事件变量让主线程挂起,进入到子线程入口函数 ThreadFunction 中,又通过 tagThreadParameter 构造体,拿到传入到参数,进入到 CAsynchronismThread::OnEventThreadStrat()函数,

因为此时 m_pIAsynchronismEngineSink 绑定到了网络引擎,所以实际上调用到时网络引擎的 OnAsynchronismEngineStart 函数。设置事件变量,好让主线程从新执行。

接着进入到线程执行函数,

到这里,异步线程就在循环里一直查看实现端口状态是否有网络操作达到。当有事件实现时,会利用临界区来对线程进行同步,避免多个线程操作队列数据。

异步引擎总结
通过以上剖析能够看到应用异步引擎有几个重要步骤:
  1. 先执行 SetAsynchronismSink 函数,将异步引擎要服务的模块和异步引擎绑定,在这个函了数中,创立了模块指定的个数的异步线程,也能够看做是工作线程,同时把每个线程都和模块绑定,也就是初始化异步引擎的的 m_AsynchronismThreadArray 线程对象数组,
  2. 执行 StartService 启动异步引擎,先是创立一个实现端口,并且把它和每个异步线程绑定,而后调用 StartThread 启动每个异步异步线程。
  3. 在线程启动函数里,创立线程参数,tagThreadParameter 构造体,通过这个构造体传递了本线程本人的线程指针,用来标记线程是否启动胜利的标记,以及一个用来保障把 cpu 工夫片执行权限让给子线程的事件句柄。
  4. 进入到 ThreadFunction 线程函数,先是调用重写了 CServiceThread 类的 OnEventThreadStrat 函数,在异步线程的 OnEventThreadStrat 函数中,会调用第一步绑定好了的异步线程的回调钩子函数 m_pIAsynchronismEngineSink,进入到对应的被绑定的服务中的 OnAsynchronismEngineStart 函数。做一些服务模块自定义的启动初始工作。
  5. 接着调用 setEvent 函数设置主线程创立的事件信号,这样主线程能够持续往下执行。而后在子线程里用一个除非执行返回为 false 才跳出的 while 循环来循环执行线程的运行函数 RepetitionRun,在异步线程的运行函数里,是期待在实现端口的 GetQueuedCompletionStatus 函数上,这个函数会让异步线程进入到不占用 cpu 的睡眠状态,直到实现端口上呈现须要解决的网络操作或者超出设定的等待时间限度为止。
  6. 能够看到异步引擎应用的是应用指定个数的工作线程来为其余模块服务,其余模块能够把工作交给异步线程,在异步线程里期待在在实现端口上,这样就不会让主线程阻塞。能够把异步引擎看做是具体对专为应用实现端口而设计的线程性能类

    网络引擎

    启动实例

    1. 通过大厅服务器启动过程来看网络引擎的启动,通过函数 CTCPNetworkEngine::StartService()启动网络引擎,

    能够看到先是创立了一个容许机器 cpu 外围个数线程调度的实现端口,这个就是网络引擎的主实现端口。而后就是调用和实现端口配套的函数,创立绑定,设置监听 socket。

    2. 在异步引擎里启动网络引擎,并且只设置了一个工作线程。通过上面对异步引擎剖析,异步引擎本人会启动一个实现端口,最终网络引擎在异步引擎里运行,期待挂起在实现端口实现告诉函数上。

    3. 创立外围个数读写线程保留到网络引擎的读写线程数组 m_SocketRWThreadArray 中,接着初始化应答线程,把应答线程与下面创立的主实现端口,监听 socket,以及网络引擎指针绑定。

  7. 启动每个读写线程,最终也是期待挂起在主实现端口的 GetQueuedCompletionStatus 函数上,读写线程重写了父类线程 RepetitionRun 运行函数,在运行函数里有实现告诉到来时先是对重叠 IO 和单句柄数据也就是连接子项类进行一些查看。

    5. 当实现告诉到来时,用 CONTAINING_RECORD 宏获取重叠 io 对应的数据包,而后依据数据包的类型进行解决。
    6. 数据发送:进入到 CTCPNetworkItem::OnSendCompleted 函数,因为是实现端口,所以看这个函数前,先看一下 CTCPNetworkItem::SendData 发送数据函数,

    其中有一个 GetSendOverLapped 函数用来获取发送重叠 IO 构造,

    能够看到优先应用 m_OverLappedSendActive 重叠构造发送数据,不够用或者残余可用长度不够时就用 m_OverLappedSendBuffer 重叠构造,最初才应用 new 来创建对象。这样能够保障服务器运行过程中根本不调用 new 开拓内存,防止产生内存碎片。能够看到一次最大发送 2046 个字节数据。接着进行加密,封包,而后应用 WSASend 函数,投递异步发送申请。
    接下来再看 CTCPNetworkItem::OnSendCompleted 函数,先是开释掉上一次发送用的重叠 IO 构造,并且保留到 m_OverLappedSendBuffer 缓冲重叠构造中,如果还有须要发送的数据,就再次投递异步发送申请。
    数据接管:进入 CTCPNetworkItem::OnRecvCompleted 函数,同样看接收数据要先看是如何投递连贯申请的,咱们通过看

    接管重叠 IO 的数据结构定义,发现并未提供接收数据的内存,构造函数里 WSABUF 接收缓冲区指针也是间接指向 NULL,第一个接收数据申请是在应答线程里投递的,前面会提到。
    正因为如此,所以在接管数据处理时,是间接调用阻塞的 recv 函数来从读取数据,接管完后就是对数据进行查看,做粘包拆包解决,而后再进行校验,最初非法的残缺数进入到网络引擎的 CTCPNetworkEngine::OnEventSocketRead 函数,这个函数再调用事件接口的 OnEventTCPNetworkRead 函数,让调度引擎进行解决。

  8. 接着启动检测线程,每隔 10s 对所有和网络引擎连贯的 socket 进行心跳,非法检测。
  9. 接着启动应答线程,在应答线程的线程运行函数里,调用的是 WSAAccept 函数,这个阻塞函数也会让应答线程挂起。当有连贯申请到来时,先判断最大连接数,而后创立新的连接子项,并与之绑定好新连贯的 ip、socket,而后把他和主实现端口绑定。接着投递一个异步承受数据申请
    9.留神用 WSARecv,WSASend 投递异步收发数据申请时,WSA_IO_PENDING 示意数据暂且还没收或者发送结束,须要期待后续告诉,所以也不能执行敞开 socket 操作。

    网络引擎总结

    通过以上剖析,发现网络引擎读写线程和应答线程绑定的是同一个实现端口,采取的是单线程解决 socket 接入,多线程解决数据收发,检测线程是另外一个线程。发送数据是异步,接收数据尽管收到告诉应用了实现端口,然而投递的接收数据申请应用 0 缓冲区,而后期待有实现告诉时,再真正接收数据,搜寻网上材料,说这种设计是因为投递申请后,会锁定缓冲区内存,即便你 WSARecv 的缓存大小是 1 字节, 被锁定的内存也将会是 4k. 非分页内存池是由整个零碎共用的, 如果用完的话最坏的状况就是零碎解体。应用 0 内存,所以就不会被锁定,然而这样读数据效率必定会升高。
    另外尽管独自启动了一个线程用来解决接入申请,然而效率应该不会比投递异步的 acceptEX 申请去接入效率高。

    调度引擎

    启动实例

    还是以大厅服务器的调度引擎启动过程阐明:调度引擎启动很简略, 应用一个工作线程的异步引擎绑定本人,而后启动异步引擎。
    异步引擎启动后,在调度引擎的异步引擎启动函数 CAttemperEngine::OnAsynchronismEngineStart()中,调用调度钩子函数启动函数,在调度钩子启动函数中,先把网络引擎和房间列表绑定,而后用外部网络服务连贯直达服务。
    当有实现告诉到来时,会进入到 CAttemperEngine::OnAsynchronismEngineData 函数,

    能够看到在这里解决了来自内核其余引擎服务的所有音讯。

调度引擎总结
  1. 调度引擎是解决整个内核所有音讯的调度核心,只管网络引擎,数据库引擎,工夫引擎,TcpSocket 服务都至多各自占用一个线程,他们都独立工作,然而最终解决这些音讯的还是调度引擎一个线程。
  2. 调度引擎能解决来自其余引擎的音讯,是因为在初始化时,其余须要投递音讯给调度引擎解决的线程都绑定了调度引擎的事件接口,当其余引擎本人线程解决完本人的工作后,通过事件接口回调调度引擎的异步线程的 CAsynchronismEngine::PostAsynchronismData 函数,通过实现端口的 PostQueuedCompletionStatus 函数被动投递实现告诉。
  3. 总的来看其余引擎相当于生产者,调度引擎相当于消费者,期间应用了数据队列来替换数据,保障音讯的先进先出,应用临界区锁来进行线程同步保障队列的音讯精确的被某一个线程解决。

    察看到异步引擎在从队列拿音讯时,只对拿的过程进行爱护,并不是解决完一条音讯后加锁,能够想到在写业务逻辑时,是须要思考到函数是否重入的。如果客户端以很快的速度发送多条同样的音讯,这时候服务器可能会出问题。

    TcpSocketService 外部网络服务

    启动流程

    通过大厅服启动来阐明:
    外部网络服务外部有一个重写的 CTCPSocketServiceThread 网络线程类,外部网络服务启动时,间接启动了网络线程。
    在线程启动函数 CTCPSocketServiceThread::OnEventThreadStrat()里,创立了一个窗口句柄,窗口不显示。
    而后再线程运行函数里,会调用阻塞的 GetMessage 函数让线程挂起直到有音讯到来。

    同时在调度引擎的异步引擎启动函数 CAttemperEngineSink::OnAttemperEngineStart 里,会应用 CTCPSocketService::Connect 函数,给外部网络服务网络线程绑定的窗口句柄投递一个连贯申请。

    此时会在网络线程的线程运行函数里解决音讯。调用 CTCPSocketServiceThread::PerformConnect 执行连贯申请。
    在执行函数 CTCPSocketServiceThread::PerformConnect 里
    ![Uploading file…]()
    建设 socket,并调用 WSAAsyncSelect 函数,把 socket 设置为非阻塞模式,并且绑定 socket 和窗口句柄,以及注册感兴趣的事件类型。这样当 socket 上有感兴趣事件到来时,WM_SOCKET_NOTIFY 音讯就会发送给 m_hWnd 窗口句柄,在线程运行函数里解决。

外部网络服务总结

1.应用外部网络服务发送数据:通过调用 CTCPSocketService::SendData 函数,这个函数有 2 个版本一个发送不带数据音讯,一个发送带数据音讯。最终都是在网络线程中调用 PostMessage 函数异步地把音讯退出到绑定的窗口句柄的音讯队列里。从而在线程运行函数里能够取出队列中的音讯。接着在 CTCPSocketServiceThread::OnServiceRequest 函数里进行发送音讯解决。最终调用到 CTCPSocketServiceThread::SendBuffer 函数,调用的是 send 函数进行音讯发送。
2.总的来说外部网络服务会解决两种大类型的音讯,一种是被动用网络线程调用 PostMessage 函数投递的 WM_SERVICE_REQUEST 服务申请,另外一种是绑定在窗口句柄上的 socket 收到的 WM_SOCKET_NOTIFY 网络音讯。

服务申请:

1.连贯申请:连贯申请会应用 WSAAsyncSelect 异步抉择模型,把 socket 绑定到窗口句柄上。
并且尝试 connect 一个地址,如果连贯失败,网络线程会读到 WM_SOCKET_NOTIFY/FD_CONNECT 网络音讯 / 网络连接音讯。把连贯的 ErrorCode 发送给调度引擎。调度引擎判断是连贯谬误,就设置一个定时器,进行下一次连贯尝试。
2.发送申请:用于外部网络服务发送数据。当调用 send 函数发送数据返回 WSAEWOULDBLOCK 谬误后,示意 socket 缓冲区已满不可写时,会调用 AmortizeBuffer 函数把数据缓存,这个时候,网络线程会收到 WM_SOCKET_NOTIFY/FD_READ 网络音讯 / 数据读取音讯,在 CTCPSocketServiceThread::OnSocketNotifyWrite 函数中再次发送数据。

网络音讯:

FD_READ:
1:当调用 WSAAsyncSelect 函数时,如果以后有数据可读。
2:当数据达到并且没有发送 FD_READ 网络事件时。
3:调用 recv()或这 recvfrom, 如果仍有数据可读里。
FD_WRITE:
1:调用 WSAAsyncSelect 函数时,如果可能发送数据时。
2:connect 或者 accept 函数后,连贯曾经建设时。
3:调用 send 或者 sendto 函数,返回 WSAWOULDBLOCK 谬误后,再次调用 send()或者 sendto 函数可能胜利时。因为此时可能是套接字还处于不可写状态,屡次调用直到调用胜利为止。
FD_CONNECT:当 connect 胜利或者失败时都会收到这个音讯,都会把错误码投递给调度引擎,如果胜利调度引擎会立刻发送一次列表音讯申请,设置定时获取列表音讯定时器。

  1. 外部网络服务是专为服务器之间外部发送接管音讯而设计的。异步抉择模型尽管接管读写告诉是异步的,然而读写数据其实还是同步的,所以性能应该比不上实现端口。

    框架源码读后感

    长处:

  2. 采纳组件模块化,从而高度复用,可扩展性强。绝大多数性能可通过读取配置或者组件配置。
  3. 整个架构利用面向对象多态性,调用方保留被调用方根底接口指针,调用方间接调用接口指针内申明的纯虚办法,而此纯虚函数的具体逻辑由该接口的派生类实现。从而很好的遵循了开闭准则和依赖倒置准则等设计准则。
  4. 代码正文清晰,变量,函数名,类名命名应用标准,代码可读性强。
  5. 整个服务器上应用多个过程,每个过程又采纳多线程,底层采纳实现端口高性能通信模型,充分发挥 cpu 性能,从而保障了整个服务器的高效。
  6. 应用 c ++,有 c ++ 编程强类型查看,高执行效率,vs 编辑器弱小调试性能的长处。

    可能存在的毛病

  7. 大量的同步调用间接操作数据库,会影响性能,能够采纳应用 redis。
  8. 尽管应用了实现端口,然而可能一些应用办法,并不是实现端口举荐应用的最佳计划。但这个可能和业务需要无关,须要思考到具体应用场景。
  9. 大量应用存储过程,使得对数据库的操作和存储不能拆散,开发时也不不便。较为不便的设计是数据库只负责存储数据,业务逻辑把要存储的数据处理好后用专门数据长久化服务来解决。
  10. 尽管可配置性强,然而房间人数也提前配置好,这个不够正当,桌子人数能够在建房规定确定后动静初始化
  11. 大厅服,房间服收发直达服数据时,应用了单线程的阻塞收发数据的 TcpSocketService, 这是 2 个服务和直达服务维持长连贯的惟一 tcp 连贯。有检测心跳,播送直达,音讯收发等性能。如果创立房间并发量高,这里可能也会是性能瓶颈。
  12. 思考到客户端是 lua,如果让服务器游戏逻辑也用 lua 来实现,可能会进步开发效率。同时也能够利用脚本语言的个性,实现游戏的热更新。
  13. 调度引擎是解决所有其余引擎的申请的惟一消费者, 然而只有一个线程, 尽管调度引擎只对从队列取音讯加锁, 在解决完取出的音讯前就曾经开释锁, 然而像房间服务, 能不能依据房间划分, 把每个房间外部再加一个队列存储本房间的音讯, 这样造成一个 2 级队列,1 级队列存储的是须要解决的房间, 而每个房间里又存储了这个房间的所有须要解决的音讯. 而后调度引擎也用多线程并发. 每次一个线程从 1 级队列 pop 一个房间, 而后再从 2 级队列 pop 一条音讯, 这样能够保障同一时刻只有一个线程解决一个房间的音讯. 然而不同线程能解决不同房间的音讯.

    本文由博客一文多发平台 OpenWrite 公布!

正文完
 0