服务端模块详解
工程概览
- 工程文件:
src-server/Server.vbp - 启动对象:
Form1 - 输出文件:
dist/服务端/Server.exe - 数据库文件:
data.mdb(Access)
全局模块
Insts.bas — 全局实例与系统日志
定义服务端全局共享的实例:
vb
Public TcpServer As cWinsock ' TCP 监听对象
Public Db As cDataBase ' 数据库连接对象
Public FileServer As cFileServer ' 文件服务器对象全局日志函数:
vb
Public Sub AddLog(ByVal Msg As String, Optional Level As EnumLogLevel = LvInfo, Optional Title As String)| 参数 | 类型 | 说明 |
|---|---|---|
Msg | String | 日志内容 |
Level | EnumLogLevel | 日志级别:LvInfo, LvWarning, LvDanger |
Title | String | 日志标题/分类 |
功能特点:
- 日志同时输出到文件(通过
VBMAN.Logs)和主窗体List2列表 - 列表最多保留 1000 条记录,超出自动移除最早记录
- 日志文件按日期自动分目录存储
窗体模块
Form1.frm — 服务端主窗体
界面布局:
List1— 左侧用户列表(显示昵称,在线用户前缀[ 在线 ]并置顶)List2— 右侧操作日志/信息列表Label1— 右上角在线客户端数量
菜单:
文件 → 开机启动— 切换 Windows 开机启动状态刷新用户— 重新从数据库加载用户列表推送公告— 打开fNotify编辑并推送公告
核心逻辑:
Form_Load:
- 创建
cWinsock实例,监听 TCP 800 端口 - 连接
data.mdbAccess 数据库 - 首次加载用户列表
LoadUsersFromDB True - 注册路由和中间件
RegRouter - 设置
Common.IsServer = True
- 创建
RegRouter — 注册中间件与路由:
vb' 中间件 Common.MiddleWares.Add New mAuth, "Auth" ' 白名单(无需验证 Token) Common.MiddleWaresWhiteList.Add 1, "User/Login" ' 业务路由 Common.Router.Add New bUser, "User" Common.Router.Add New cMessage, "Message" Common.Router.Add New bNotify, "Notify" Common.Router.Add New bCalc, "Calc"LoadUsersFromDB — 加载用户列表:
- 参数
Force=True时重新执行 SQL 查询 - 使用静态变量
Users缓存查询结果,避免频繁查库 - 遍历用户,通过
mTcp.ExistsUser(x("UserName"))判断在线状态 - 在线用户添加
[ 在线 ]前缀并插入到列表顶部(AddItem ..., 0)
- 参数
Tcp 事件:
ConnectionRequest— 新客户端接入(可用于 IP 白名单控制)ClientCountChange— 客户端数量变化时更新状态栏和在线列表DataArrival— 调用Common.HandleReciver分发请求CloseEvent— 客户端断开
fNotify.frm — 公告编辑器
界面元素:
Text1— 公告标题Text2— 公告内容(多行文本)Command1— 立即推送按钮Command2— 清空按钮Label3— 显示上次推送时间
核心逻辑:
Form_Load:
- 从
config.ini读取已保存的公告内容并填充
- 从
Command1_Click (推送):
vbVBMAN.Ini.Section("Notify")("Time") = Format(Now, "yyyy年MM月dd日 hh:mm:ss") VBMAN.Ini.Section("Notify")("Title") = Text1.Text VBMAN.Ini.MultiLineText("Notify", "Content") = Text2.Text VBMAN.Ini.SaveTo Common.SendToAll Insts.TcpServer, "Notify/Show", VBMAN.Ini("Notify")公告保存到 INI 文件后,向所有在线客户端广播。
Command2_Click (清空):
- 删除 INI 中的公告节点
- 发送空公告到所有客户端,实现"撤回"效果
日志系统
服务端采用多级日志设计,分为全局系统日志和业务专用日志两类:
- 全局系统日志:
Insts.AddLog— 记录系统启动、客户端连接/断开、登录认证等事件 - 业务专用日志: 各模块独立的
cLogs实例 — 记录业务数据(如文件传输、计算参数、公告推送等)
详细说明请参考 日志系统详解。
业务类
bUser.cls — 用户登录验证
| 方法 | 说明 |
|---|---|
Login(Inst, Data) | 处理客户端登录请求,验证账号密码,生成 Token,绑定用户 |
ChangePassword(Inst, Data) | 处理客户端修改密码请求,验证旧密码并更新数据库 |
登录验证流程:
- 从
Data中提取username和password - 查询数据库:
select * from users where username='xxx' - 验证用户名是否存在、密码是否设置、密码是否匹配
- 如果该账号已在线 → 向旧客户端发送"账号在另外一个地方登录",并强制断开 (
CloseUser) - 生成 GUID 作为 Token
- 绑定用户到连接:
TcpServer.BindUser UserName, Inst, Token, UserData - 刷新服务端用户列表
Form1.LoadUsersFromDB - 返回用户信息给客户端:
SendTo Inst, "User/Info", .Row
修改密码流程 (ChangePassword):
- 从
Data中提取oldPassword和newPassword - 获取当前连接绑定的用户名
Inst.CurrentUser和客户端 IP - 参数验证:
- 旧密码或新密码为空 → 拒绝,记录日志
- 数据库验证:
- 查询用户信息,用户不存在 → 拒绝,记录详细日志(包含提交的账号、密码、IP)
- 验证旧密码,不匹配 → 拒绝,记录日志(包含提交的密码、IP)
- 更新密码:
- 执行 SQL:
update users set password='xxx' where ID=xxx - 发送 Toast 通知客户端"密码修改成功"
- 记录成功日志(包含用户名、IP)
- 返回更新后的用户信息
- 执行 SQL:
安全日志记录:
| 场景 | 日志级别 | 记录内容 |
|---|---|---|
| 参数为空 | Warn | 用户名、IP |
| 用户不存在 | Danger | 提交的账号、旧密码、新密码、IP |
| 旧密码错误 | Warn | 用户名、提交的旧密码、IP |
| 修改成功 | Info | 用户名、IP |
数据库表结构 (users):
| 字段 | 类型 | 说明 |
|---|---|---|
ID | AutoNumber | 主键 |
UserName | Text | 登录账号 |
NickName | Text | 显示昵称 |
Password | Text | 哈希后的密码 |
bNotify.cls — 公告查询
| 方法 | 说明 |
|---|---|
CheckNew(Inst, Data) | 客户端启动后调用,查询是否有离线期间的公告 |
逻辑:
- 如果 INI 中存在公告 → 将公告内容发送给请求客户端
- 如果没有公告 → 发送空对象,客户端显示"暂无公告..."
bCalc.cls — 参数计算处理
| 方法 | 说明 |
|---|---|
Submit(Inst, Data) | 接收客户端发送的 50 个参数,模拟修改后回传 |
模拟修改示例:
vb
Data("id") = 1000
Data("memo") = Data("memo") & vbCrLf & "这是服务端追加的备注内容"
Data("remark") = "你可以修改更多的字段"
Common.SendTo Inst, "Calc/Show", Data.Root实际业务中可以在此进行真实的计算、数据校验、持久化等操作。
中间件
mAuth.cls — 授权验证中间件
设计理念: 中间件是前置拦截器,所有请求(除白名单外)在进入业务逻辑前都必须通过中间件校验。
接口定义:
vb
Public Function Entry(Inst As cWinsock, Req As cJson, Optional LastError As String) As Boolean| 参数 | 说明 |
|---|---|
Inst | 当前客户端连接实例 |
Req | 客户端发来的完整 JSON 请求对象 |
LastError | 若返回 False,此参数携带错误信息回传给客户端 |
校验规则:
Req("token") = ""→ 错误: "凭证为空"Inst.CurrentUserToken = ""→ 错误: "当前客户端未绑定用户"Inst.CurrentUserToken <> Req("token")→ 错误: "凭证验证失败"- 全部通过 → 返回
True
扩展中间件: 可按相同模式新增中间件类,在 Form1.RegRouter 中添加到 MiddleWares 集合即可实现链式调用。
数据库操作
服务端使用 VBMAN 的 cDataBase 对象操作 Access 数据库:
vb
Dim mDB As New cDataBase
mDB.Connect Access, App.Path & "\data.mdb"
' 查询单条记录
mDB.Sql("select * from users where username='wangli'").Fetch
If mDB.Row.Count > 0 Then
Debug.Print mDB.Row("NickName")
End If
' 查询多条记录
mDB.Sql("select * from users").Query
Dim Users As New cJson
Users.Decode mDB.Rs常用方法:
| 方法 | 说明 |
|---|---|
.Connect Access, 路径 | 连接 Access 数据库 |
.Sql("...") | 设置 SQL 语句 |
.Fetch | 执行并获取单条记录到 .Row |
.Query | 执行并获取记录集到 .Rs |
.Row.Count | 返回记录数(0 表示无记录) |