Преглед изворни кода

Z991239-351 #comment 将设计文档放到这里

gifur пре 5 година
родитељ
комит
320c4ca98d

+ 4 - 0
README.md

@@ -1,3 +1,7 @@
+# 招行可视柜台终端应用程序
+
+**可视柜台终端应用程序(Remote Video Counter Terminal Application)**是基于C/C++实现的运行在Windows平台上的异步调用、多进程通信的 Win32 位 Debug 版应用程序。
+
 ## 目标
 
 重构和移植可视柜台终端框架以实现跨平台化,并新增特性最终打造成一个轻便的分布式容器类框架

+ 16 - 0
doc/designs/component_and_struture/component_IO_manager.md

@@ -0,0 +1,16 @@
+# sp_iom_t
+
+## 生命周期
+
+在 SpModule::Init中创建
+
+## 依赖关系
+
+
+
+执行轮询的功能,主要有连个模块,一个是BUS终端,一个是计时器列表。
+
+
+
+有一个成员变量为BUS终端,用于发送和接收请求
+

+ 19 - 0
doc/designs/component_and_struture/component_RPC.md

@@ -0,0 +1,19 @@
+# p_rpc_server_t
+
+蓝屏、对实体的控制都是通过此通道进行处理的。
+
+* 
+
+## 客户端
+
+## 服务端
+
+## 生命周期
+
+* SpAsyncWaitRPC
+* 
+
+## 依赖关系
+
+* 依赖服务组件
+* 

+ 33 - 0
doc/designs/component_and_struture/component_broadcast.md

@@ -0,0 +1,33 @@
+# BCM
+
+## 生命周期
+
+### sp_bcm_daemon_t
+
+* spshell
+  * SP_PKT_BCM::daemon_on_pkt
+  * daemon_on_sys
+
+### sp_bcm_listener_t
+
+* SpEntity::SubscribeBroadcast
+  * sp_bcm_listener_create
+  * sp_bcm_listener_subscribe
+* 
+
+### sp_bcm_client_t
+
+* SpEntity::Init()
+* SpEntity::SendBroadcast
+
+
+
+## 结构信息
+
+### sp_bcm_listener_t
+
+* 目标实体ID
+* 参数值
+* 是否订阅
+
+## 依赖关系

+ 97 - 0
doc/designs/component_and_struture/component_bus_daemon.md

@@ -0,0 +1,97 @@
+# 总线篇
+
+总线分两个模块,分别是用于后台分发的**daemon**实体调用的**bus_ept**
+
+
+
+通过**spshell**创建一个命名管道作为服务端接收来自模块的连接
+
+## 初始化流程
+
+### 服务端
+
+1. 创建**bus_daemon_t**对象;
+
+2. 创建**ioqueue_t**对象;
+
+3. 创建接收器;
+   1. 对应每个url创建一个daemon_accetpor_t
+   2. 分配 ioqueue_overlapped_t*,该结构体定义为32个指针
+   
+4. 接收器绑定重叠IO进行监听,默认是5个
+
+5. 在Windows上,创建多个线程,用于接收IOCP的事件并对事件根据类型进行处理,因为IO重叠的实现机制,Windows上可以开多个线程处理请求,系统自动会维持调度的平衡。
+
+6. 一个初始的请求过来即创建一个**endpt_session_t**,后续进行发送和接收操作
+
+   1. 过来的客户端先加入到未注册列表
+   2. 收到BUS_TYPE_ENDPT_REGISTER事件后将其加入到注册列表
+
+   
+
+### 客户端
+
+1. 根据 url对象创建 SOCKET或者管道句柄;
+2. 尝试连接
+3. 发送 BUS_TYPE_ENDPT_REGISTER 注册事件
+4. 
+
+## 会话
+
+* 会话分为注册和未注册两种状态,通过**epid**进行标识,该标识在从未注册状态转向注册状态时才有效;
+* 新创建的会话处于未注册状态;
+* 每一次会话掉线或者上线(注册)都会通知其他同行**Session**
+
+### 通讯方式
+
+* 管道(TYPE_PIPE)
+* TCP(TYPE_TCP)
+
+## 接收器(acceptor)
+
+* 用于初始化**会话**对象;
+* **uri**决定了创建接收器的个数和接收器的类型
+  * 类型:
+    * HANDLE_TYPE_ACCEPTOR
+    * HANDLE_TYPE_PIPEACCEPTOR
+
+## 重叠IO
+
+* 所有的发送和接收行为都是通过**重叠IO异步操作**来完成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 46 - 0
doc/designs/component_and_struture/component_configuration.md

@@ -0,0 +1,46 @@
+# 配置篇(sp_cfg)
+
+## Shell.ini
+
+### 实体清单([Entity])
+
+```
+[Enity]
+//实体名称=权限等级,模块名称[.dll],开发ID(devel_id)
+//可以一个模块中配置多个实体
+HelloClient=0,mod_helloclient.dll,0x901
+```
+### 启动实体([Startup])
+
+;配置自启动实体
+
+```
+Number={数量}
+{序号{1...数量}}={实体名称,对应实体清单中的实体名称}[{空格}实体启动参数字符串]
+
+```
+
+
+
+
+
+```
+
+```
+
+### 系统事件([SysEvent])
+
+```
+[SysEvent]
+//系统事件名称=实体名称,初始值
+//支持多个实体,用";\t "进行区分
+HelloState=HelloService,"0"
+```
+
+```
+[Main]
+ConsolePort={控制台服务的端口号}
+[Debug]
+SpShell={调试等级,只能1或者2,目前的功能来看只是用于区别能否多写点日志}
+```
+

+ 91 - 0
doc/designs/component_and_struture/component_entity_module.md

@@ -0,0 +1,91 @@
+
+
+# 模块篇(sp_mod)
+
+## 初始化
+
+通过**配置组件**中的配置信息,生成模块和实体对象
+
+## Entity与 mod 的关系
+
+* 一个 **mod** 可以对应多个 **Entity**,在 GUIConsole 窗口看到的所有条目都是实体的概念,包括状态也是实体的状态
+* **epid**: 模块在从配置中读取时的实际序号(sp_cfg_shell_module_t::idx),暂且扩写为 **entity parent id**
+* **mod** 是一个进程,**entity** 必须在 **mod** 加载后才能进行创建。
+
+
+
+
+
+## 实体可订阅事件
+
+* 生命周期
+  * 创建
+  * 关闭
+  * 异常
+* 状态转换
+  * 实体状态
+
+
+
+## 模块的启动来源(进程建立)
+
+* **app::on_req**:
+* **CSpShellConsole::StartEntity**:
+
+## 实体状态
+
+* 停止实体
+  * 前提:BUSY | IDLE | Pause
+* 暂停实体
+  * 前提:BUSY|IDLE
+* 继续实体
+  * 前提:PAUSE
+* 握手实体
+  * 前提:	IDLE|BUSY|PAUSE
+
+卸载模块与终止模块的区别在于:后者先杀死模块进程,然后将所属实体置为kill状态,而前者要判断一下当前的模块中的实体状态,如果有处于正常运行状态的,则不作任何操作
+
+## 主要关联代码
+
+```
+kickoff_startlist()::app.cpp
+load_module()::sp_mod
+create_module_process()::sp_mod
+```
+
+
+
+## 依赖关系
+
+* 对于实体,依赖服务组件,用于调用操作实体的处理函数(**sp_mod_entity_stub_t**)
+* 对于sp_mod_mgr_t,依赖服务组件,用于调用实体状态变动的函数(**mgr_on_pkt**)
+* 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 13 - 0
doc/designs/component_and_struture/component_entity_privilege.md

@@ -0,0 +1,13 @@
+# 实体权限
+
+### 普通权限
+
+### 高级权限(SpEntityPrivilege)
+
+- 控制实体
+
+
+
+## 依赖关系
+
+依赖RPC

+ 16 - 0
doc/designs/component_and_struture/component_message_listener.md

@@ -0,0 +1,16 @@
+ioqueue_t::msg_handlers
+
+* 消息类型
+* 消息等级
+* 回调函数(处理函数)
+
+sp_iom_t::arr_pkt_handler
+
+pkt_handler_t
+
+* Key(对象地址)
+* 处理函数
+* 用户数据
+
+sp_svc_t::pkt_handler_list
+

+ 55 - 0
doc/designs/component_and_struture/component_multi-thread.md

@@ -0,0 +1,55 @@
+# 线程池篇
+
+## 线程类型
+
+看是否支持多线程,不支持的情况下仅开启一个固定线程,支持多线程时才有看点:
+
+* 固定线程
+  * 初始时数量设为1个;
+* 临时线程
+  * 支持的最大数量为32个;
+* 线程最大存活时间:5分钟
+
+## 线程方法签名
+
+* 带一个参数
+* 带三个参数
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 23 - 0
doc/designs/component_and_struture/component_remote_control.md

@@ -0,0 +1,23 @@
+可视柜台终端可支持通过远程命令行进行控制。要激活此功能,需要在shell.ini中做相关的配置
+
+一个连接ID可以对应多个实体
+
+常用指令:
+
+* ?/help
+* start {EntityName} // 带参数,实体名称或者实体ID
+* stop {EntityName} // 带参数,实体名称或者实体ID
+* pause {EntityName} // 带参数,实体名称或者实体ID
+* continue {EntityName} // 带参数,实体名称或者实体ID
+* terminate {EntityName} // 带参数,实体名称或者实体ID
+* lost {EntityName} // 带参数,实体名称或者实体ID
+* list //列举所有实体
+  * 所有实体,包括没启动的,不包括spshell,特殊的实体其ID为0
+  * 实体名称,实体ID,进程ID,实体状态,占用内存(MB)
+* shutdown //退出SpShell进程
+* bluescreen ///
+* unbluescreen //
+* setlogon // 带参数,实体名称或者实体ID
+* setlogoff //
+* sysvar //打印框架的系统变量名称和相应的值
+* 

+ 47 - 0
doc/designs/component_and_struture/component_services.md

@@ -0,0 +1,47 @@
+# 服务篇
+
+## 生命周期
+
+在 SpEntity::Init() 中被创建
+
+## 依赖关系
+
+
+
+## 模块组件
+
+### 线程池 threadpool
+
+直接支持多线程,固定线程数1个,最多支持32个线程;
+
+### 计时器管理器 tmr_mgr
+
+### 事件处理器
+
+* **svc_id**:实体在配置模块中的数组下标;
+
+- 重点函数:**on_rx_pkt**
+- 执行逻辑:针对事件类型,依次执行事件类型中对应的所有事件处理函数,直至出现如下两种情况:
+  - 遍历完成
+  - 过程中出现:处理函数返回0,
+
+### 处理类型
+
+* pkg
+* sys
+* info(具体操作)
+  * 蓝屏
+  * 严重信息显示
+  * 启动信息显示
+  * 机器重启
+* req
+  * 启动实体;
+
+## 处理机制
+
+* 其他的每个重要的模块都会添加对应事件的回调处理函数到服务模块;
+* 当消息发生时会遍历调用每个回调处理函数
+
+## 依赖关系
+
+* 从IO管理层中获取事件

+ 21 - 0
doc/designs/component_and_struture/component_session.md

@@ -0,0 +1,21 @@
+# 会话篇
+
+用于实体与实体之间的通讯
+
+## 客户端
+
+* 会话管理:sp_ses_mgr_t
+* 会话客户端对象:sp_ses_uac_t
+* 方法请求客户端对象:sp_tsx_uac_t
+  * 方法ID
+  * 方法签名
+* 状态
+  * 初始化(SP_SES_STATE_INIT)
+  * 连接中
+    * 超时计时器
+  * 已连接
+
+## 依赖组件
+
+* iom:请求超时所使用的计时器
+  * 

+ 27 - 0
doc/designs/component_and_struture/component_system_log.md

@@ -0,0 +1,27 @@
+# 日志篇
+
+
+
+## 生命周期
+
+### sp_log_daemon_t
+
+* spshell
+
+### sp_log_client_t
+
+### sp_log_listener_cb
+
+### sp_log_listener_mgr_t
+
+* SpEntity::Init()
+* 
+
+* **log.cpp::on_log**: 具体的日志操作。输出打印到启动界面,GUIConsole显示输出,打印到日志文件。
+
+## 详细信息
+
+* 每条日志长度的限制的1023
+* LOG_CMD_RECORD
+  * 收到消息后塞到包队列中并释放信号量
+  * 

+ 26 - 0
doc/designs/component_and_struture/component_system_variables.md

@@ -0,0 +1,26 @@
+# 系统变量篇
+
+## 生命周期
+
+### sp_var_client_t
+
+* SpEntity::Init中创建
+
+### sp_var_listener_t
+
+* SpEntity::RegistSysVarEvent中创建
+
+## 具体实现
+
+* 非拥有者是无权限写系统变量的;
+* 系统变量名称的长度不能超过260;
+* 一个实体系统变量监听的个数不超过127个;
+
+## 系统变量监听
+
+* 注册每一个变量监听时创建一个sp_var_listener_t,并在svc层订阅SP_PKT_VAR事件;
+
+## 依赖关系
+
+
+

+ 24 - 0
doc/designs/component_and_struture/component_upper_module.md

@@ -0,0 +1,24 @@
+我们所看到的实体
+
+## 实体与实体间的通讯连接
+
+* CClientSessionBase
+
+* SpEntity::ConnectRemoteEntity
+* SpClientSessionFunction:继承于IClientSessionFunction 和 SpAsyncWait,依赖 **会话**组件
+  * CClientSessionBase 通过 GetFunction() 可以得到它
+* ses::on_accept
+* SpEntity::on_accept
+* CServerSessionBase
+* SpServerSessionFunction
+
+## 实体与实体间请求方法的调用
+
+* SpClientSessionFunction::AsyncRequest
+* SpAsyncWaitTsx
+* 
+
+## 依赖组件
+
+* 会话组件
+

+ 12 - 0
doc/designs/component_and_struture/instruct_run_serial_no.md

@@ -0,0 +1,12 @@
+# sp_rsn_context_t
+
+## 生命周期
+
+## 结构信息
+
+* 深度
+* 类型
+* 当前序列号:sp_uid_t
+* 前一个序列号
+
+  

+ 61 - 0
doc/designs/component_and_struture/instruct_send_package.md

@@ -0,0 +1,61 @@
+* **BUS_TYPE_PACKET**
+  * 关联
+    * bus::bus_endpt_send_pkt
+    * bus_deamon::on_process_pkt
+  * 头结点:
+    * {包类型}
+    * 用户类型
+    * 发送包所在的客户端ID
+    * 将要发送的客户端ID
+  * 内容节点:
+* **SP_PKT_SES|SES_CMD_REQ**
+  * 头部
+    * tsx_id
+    * 方法名ID
+    * 方法签名
+    * 超时时间
+    * 序列号上下文
+* **SP_PKT_VAR|VAR_CMD_CHANGE**
+  * 系统变量
+  * 内容:
+    * svc id
+    * key值
+    * 旧的值
+    * 新的值
+* **SP_PKT_BCM|BCM_CMD_MSG**
+  * 广播
+  * 首部
+    * 消息ID
+    * 消息签名
+    * 
+
+* **SP_PKT_LOG|LOG_CMD_LISTEN_RECORD**
+  * 日志
+* **SP_PKT_LOG|LOG_CMD_SUBSCRIBE**
+  * 是否关注消息体内容
+  * 日志类型
+  *  感兴趣实体ID
+  * 严重等级
+  * 系统错误码
+  * 用户错误码
+
+* **SP_PKT_LOG|LOG_CMD_RECORD**
+  * 首部(过来spshell后增加的)
+    * 包ID
+    * 包类型
+    * SVC ID
+    * epid
+  * ​	内容
+    * 实体实例ID
+    * 日志ID(0好像)
+    * SVC序列上下文
+    * 记录时间
+    * 日志类型
+    * 本地ID
+    * 本地 bus_endpt ID
+    * 错误等级
+    * 系统错误码
+    * 用户错误码
+    * 参数数量
+    * 参数值
+    * 可变内容

+ 204 - 0
doc/designs/starup_routine.md

@@ -0,0 +1,204 @@
+# 程序运行逻辑简要流程
+
+## 运行安装
+
+* 当前运行的版本须与 **install.ini** (如果该文件存在并位于版本根目录)当前版本一致
+
+## 实体启动
+
+* 等待一个实体启动的最长时间为**2分钟**(Starting的过程)
+* 等待一个实体终止的最长时间也是**2分钟**(UnLoading的过程)
+
+### SpShell 初始化
+
+```c
+int app_init()
+```
+
+1. 创建并显示启动界面
+2. 申请共享内存空间16MB(shm)
+3. 初始化环境(env)
+   1. **分配对象内存后放入到共享内存**
+   2. 从共享空间中初始化 url
+   3. 初始化组成的文件路径信息(sp_dir_t)
+      1. 主要对终端程序下各模块的路径进行赋值,供后续提取用
+   4. 解析主要文件的内容信息(sp_cfg_t)
+      1. 加载root.ini
+         1. 加载终端信息,终端号,机型等;
+         2. 加载配置的路径信息
+      2. 加载shell.ini
+         1. 读取实体节点,实体名称只能是数字或字母(sp_cfg_shell_entity_t*)
+         2. 读取模块(类库)的版本信息(sp_cfg_shell_module_t*)
+         3. 读取系统变量(sp_cfg_shell_sysevent_t*)
+         4. 读取自启动实体列表(arr_startlist)
+         5. 读取实体的调试(Debug)标识(debug_level)
+      3. 加载(如果不存在则创建)install.ini文件
+   5. 创建实体管理器(sp_mod_mgr_t)
+   6. 将实体配置信息加载到实体管理器(sp_mod_t,sp_entity_t)
+   7. 获取和记录应用启动时间(btr)
+4. 创建相关组件
+   1. 创建并启动总线守护模块(bus_deamon)
+      1. 创建 ioqueue_t 对象
+         1. 创建未绑定的IO端口对象
+      2. 根据url创建接收器对象,url分两种,管道和TCP
+      3. 接收到对端的句柄,并创建一个 end_session_t 对象加入到 非注册列表(unregistered_session_list)
+      4. 收到注册事件 BUS_TYPE_ENDPT_REGISTER
+         1. 加入 已注册列表(registered_session_list),并对已在列表中的session发送新成员的epid
+   2. 创建IO管理器(sp_iom_t)
+      1. 创建属于**spshell**的对端接收器(bus::bus_endpt_t)
+         1. 发送注册消息
+      2. 创建计时器堆(timer_queue_t*)
+   3. 创建并启动服务实例(sp_svc_t)
+      1. 创建线程池(threadpool)
+      2. 创建计时器管理(tmr)
+      3. 初始化包处理器列表
+   4. 初始化实体管理器(sp_mod_mgr_t)
+      1. 启动进程监控器(无操作)
+      2. 在SVC中注册实体管理器的SP_PKT_MOD类型事件的回调函数
+   5. 创建日志管理(log_t)
+      1. 创建日志后台守护对象
+   6. 创建变量模块(var)
+   7. 创建广播管理模块(bcm)
+   8. 创建并启动RPC服务端(rpc)
+   9. 创建并启动Sliverlight代理服务端
+5. 启动自启动实体(线程)
+   1. 根据自启动列表依次执行(cfg)
+   2. 加载模块
+      1. 创建进程
+      2. 获取模块的Session在线状态,**4分钟的时间超时**
+      3. **发送 MOD_CMD_INIT 指令**
+      4. 加入到进程监视器中(process_monitor_t)
+   3. 加载实体
+6. 启动控制台服务端
+
+```c
+int app_run()
+```
+
+执行 sp_iom_run
+
+## 模块初始化
+
+### 前提
+
+#### sphost.exe的启动
+
+1. 设置当前进程所属的环境变量
+2. 调用Spbase中的**SpRun**
+
+#### 模块开发定义
+
+1. 开发模块(.dll)时通过宏声明实体的类型和数量(SpEntityModuleStub)
+
+### 模块初始化
+
+2. 从环境中获取实体的信息(env,mod_mgr,cfg)
+3. New一个 SpModule 对象
+4. 加载模块DLL,DLL进入函数设置入口函数和出口函数(即ModuleBase::Init())和全局变量 s_pModuleInst
+5. 初始化模块(SpModule::Init)
+   1. 创建IO管理器
+      1. 创建对端接收器(bus::bus_endpt_t)
+         1. 初始化事件回调函数
+            1. BUS_RESULT_DATA -> iom::on_pkt;
+            2. BUS_RESULT_INFO -> iom::on_pkt;
+            3. BUS_RESULT_EVENT -> iom::NULL;
+            4. BUS_RESULT_SYSTEM -> iom::on_sys;
+            5. BUS_RESULT_MSG -> iom::on_msg;
+      2. **发送注册请求(BUS_TYPE_ENDPT_REGISTER)并得到回应**;
+   2. 创建日志客户端(sp_log_client_t);
+      1. local_svc_id=**SP_INVALID_SVC_ID**
+   3. 创建一个模块桩(sp_mod_stub_cb)并添加包处理器(mod_on_pkt)到IO管理器;
+6. 循环执行吞吐操作(SpModule::Run(),底层是sp_iom_run) 
+   1. 总线连接端吞吐查询;
+   2. 计时器吞吐查询;
+   3. sp_iom_run 函数在sphost 和 spshell中都会调用
+7. 收到注册实体的**MOD_CMD_INIT**通知
+   1. 触发处理函数mod_on_pkt
+   2. sp_mod_stub_t::__on_module_init
+   3. SpModule::on_module_init()
+   4. 开始注册模块下的实体
+      1. 针对逐个实体调用 ModuleBase::RegistEntity(从ModuleBase::Init()进入)
+      2. 根据权限优先级别生成实体功能类(SpModule::AddEntityBase)
+   5. 将结果通过 MOD_CMD_MOD_RESULT 返回
+
+## 实体初始化
+
+### 注意:
+
+1. 实体对象的创建,是在DLL被加载进来的时候就New出来了,在Module全局变量的构造函数实现;
+2. 实体与模块是共用一个**iom**实例对象的;
+3. 模块与实体是一对多的关系
+
+### 初始化过程
+
+8. 初始化实体功能类(SpEntity)
+   1. 创建服务实例(svc)
+   2. 设置线程池装饰器
+      1. 将当前实体对象**预**设置为线程实体(线程局部存储TLS)
+   3. 再创建一个线程池(m_tpool);
+      1. 将当前实体对象**预**设置为线程实体(线程局部存储TLS)
+   4. 创建一个实体桩(sp_mod_entity_stub_cb)
+      1. 赋值回调函数
+         1. 启动前(SpEntity::__on_entity_prestart::OnPreStart)
+         2. 停止(SpEntity::on_entity_stop::OnPreClose)
+         3. 暂停前
+         4. 握手(SpEntity::on_entity_test::OnSelfTest)
+         5. 继续前
+         6. 重定向订阅(SpEntity::on_entity_redirect_subscribe::OnBroadcastSubscribe)
+      2. 用户数据(实体对象指针)
+      3. 将SP_PKT_MOD包类型事件的处理函数加到服务层(svc)
+   5. 创建并启动RPC客户端管理对象(m_rpc_mgr)
+      1. 回调函数(SpEntity::__on_rpc_request)
+      2. 将包和系统类型事件的处理函数加到服务层(svc)
+      3. 创建并启动会话管理对象(sp_ses_mgr_t),只有实体才有这
+      4. 回调函数(__on_accept)*//调用OnNewSession接口*
+      5. 将包和系统类型事件的处理函数加到服务层(svc)
+   7. 创建日志客户端(m_ent->cfg->idx);
+   8. 创建一个变量客户端(var)
+   9. 创建一个广播客户端(bcm)
+   10. 创建日志监听管理器对象(sp_log_listener_mgr_t)
+       1. 回调函数(on_log)
+   11. 启动日志监听管理器对象
+       1. 将包类型事件的处理函数加到服务层(svc)
+
+## 其他的启动
+
+
+
+## Sphost初始化
+
+### 获取内存空间地址
+
+1. 申请共享内存空间并返回地址;
+
+### 执行模块进程
+
+1. 设置进程环境变量;
+2. 
+
+
+
+
+
+## 实体会话注册
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

BIN
doc/designs/视频柜台架构设计-黄广秦.doc


+ 8 - 0
doc/test_case/test_entity.md

@@ -0,0 +1,8 @@
+# 实体测试
+
+## 功能性测试
+
+1. 实体库文件不存在;
+2. 实体库依赖的库文件不存在;
+3. 实体`OnPreStart`接口启动失败;
+4. 

+ 25 - 0
doc/tutorials/entity_dev.md

@@ -0,0 +1,25 @@
+# 实体开发
+
+设计之初是一个模块作为一个单独的进程运行,模块中有个实体列表,模块与实体(Entity)是一对多的关系,每个模块中至多可包含32个实体。
+
+现在我们通常实现为一个模块对应一个实体,渐渐地就将模块称为实体了。
+
+实体需要继承 `CEntityBase`并实现`OnNewSession`接口,该函数返回一个会话对象,在对方第一个连接过来时会调用该函数返回一个实际的会话对象。**前提是`IsService()`必须返回 true**
+
+## 实体接口执行流程
+
+1. ``
+2. `void OnSarted()` *(SpEntity::FinishStart)*
+
+
+
+## 实体静态注册宏
+
+```c++
+SP_BEGIN_ENTITY_MAP()
+	SP_ENTITY(C{EntityName}Entity)
+    ...
+SP_END_ENTITY_MAP()
+```
+
+该操作会声明一个继承`ModuleBase`的子类并定义一个全局变量,并将实体注册到实体列表中,在当前类库被**sphost**加载时会自行对实体列表中的实体逐一进行初始化。