Unity 3D 笔记(十八)Unity/C# Socket 网络笔记7

本篇核心知识点: C# 服务端 MySQL 登录注册完整业务笔记;ORM 框架数据表两种创建方式、UserManager 用户管理类、注册 / 登录数据库增查逻辑、MySql 环境版本坑、前后端 CS/SC 登录注册协议、客户端回调分发、项目完整联调流程、开发避坑规范

一、ORM 框架数据表创建两种方式

1 概念

MySql OR 框架支持两种建表方案,一是 Navicat 可视化手动建表,二是代码调用框架 API 根据实体类自动生成数据表,适配不同开发场景。

2 特性 / 规则

  1. 手动建表:适合初期调试,可自由设置主键、自增、非空约束;

  2. 代码自动建表:正式项目批量生成表,实体修改后一键同步;

  3. 实体类字段名、数据类型必须和数据表完全匹配,否则映射失败;

  4. 自动建表仅生成不存在的表,不会覆盖已有数据。

3 语法 & 代码片段

// 框架自动根据实体生成数据表 MySqlManager.Instance.DB.CreateTableIfNotExist<UserEntity>();

4 相关知识点对比

手动可视化建表 VS 代码自动建表

手动建表:适合学习阶段,直观看到字段约束,少量表效率高; 自动建表:大型项目几十张表统一初始化,部署无需手动操作数据库。

5 模块拓展

上线初始化脚本,程序启动自动创建所有游戏业务表(玩家、背包、道具)。

二、UserManager 用户管理单例类

1 概念

封装所有账号相关数据库操作,统一提供注册、登录对外调用方法,隔离数据库底层查询代码,业务逻辑只调用接口不手写 SQL。

2 特性 / 规则

  1. 全局单例设计,服务内任意位置可访问;

  2. 所有数据库操作包裹 try-catch 捕获异常,单条账号错误不崩溃服务;

  3. 注册逻辑:先查询用户名是否存在,不存在插入数据;

  4. 登录逻辑:先查询账号,匹配密码后更新登录时间。

3 语法 & 代码片段

public static UserManager Instance { get; private set; } // 注册对外接口 public RegisterResult Register(string name, string pwd){ try{ var count = MySqlManager.Instance.DB.Count<UserEntity>( x => x.UserName == name); if (count > 0) return RegisterResult.AccountExist; UserEntity user = new UserEntity(){ UserName = name, Password = pwd, LoginTime = DateTime.Now }; MySqlManager.Instance.DB.Insert(user); return RegisterResult.Success; } catch{ return Register.Fail; } }

4 相关知识点对比

分散数据库查询 VS 统一管理类封装

分散查询:业务脚本到处写 SQL 查询,代码重复、维护困难; 统一 UserManager:账号逻辑集中一处,修改校验规则只改一处。

5 模块拓展

新增修改密码、查询玩家基础信息等扩展接口。

三、注册业务完整数据库逻辑

1 概念

客户端发送账号密码 CS 协议,服务接收后查询数据库用户名,重复返回账号已存在回执,无重复插入玩家数据并返回注册成功。

2 特性 / 规则

  1. Count 统计匹配用户名行数,大于 0 判定账号占用;

  2. 插入实体携带当前登录时间,记录首次注册上线;

  3. 所有数据库操作捕获异常,异常直接返回注册失败;

  4. 数据库插入完成后生成 SC 回执消息下发客户端。

3 语法 & 代码片段

// 统计同名账号数量 long sameUser = MySqlManager.Instance.DB.Count<UserEntity>( u => u.UserName == inputName); if(sameUser > 0){ return RegisterResult.AccountExist; } // 插入新玩家数据 UserEntity newUser = new UserEntity(); newUser.UserName = name; newUser.Password = pwd; newUser.LoginTime = DateTime.Now; MySqlManager.Instance.DB.Insert(newUser); return RegisterResult.Success;

4 相关知识点对比

先查后插注册逻辑 VS 直接插入捕获唯一键报错

先查后插:逻辑清晰,错误类型精准区分账号重复; 直接插入:依靠数据库唯一约束报错,无法精准区分失败原因。

5 模块拓展

注册时增加验证码校验、防重复短时间高频注册限流。

四、登录业务完整数据库逻辑

1 概念

客户端上传账号密码,服务查表匹配账号,账号不存在 / 密码错误返回对应失败码,匹配成功更新本次登录时间,下发登录成功回执。

2 特性 / 规则

  1. 根据用户名查询单条玩家实体;

  2. 实体为空 = 账号不存在;

  3. 实体存在但密码不匹配 = 登录密码错误;

  4. 校验通过自动更新 LoginTime 为当前系统时间。

3 语法 & 代码片段

public LoginResult Login(string name, string pwd){ try{ UserEntity user = MySqlManager.Instance.DB.FirstOrDefault<UserEntity>( x => x.UserName == name); if(user == null) return LoginResult.UserNotExist; if(user.Password != pwd) return LoginResult.PwdError; // 更新登录时间 user.LoginTime = DateTime.Now; MySqlManager.Instance.DB.Update(user); return LoginResult.Success; } catch{ return LoginResult.Fail; } }

4 相关知识点对比

单次查询实体 VS 分两次查账号、查密码

单次 FirstOrDefault:一次数据库 IO,性能更高; 两次查询:两次数据库访问,延迟更高。

5 模块拓展

增加登录 Token 生成、过期校验逻辑,实现免重复登录。

五、服务端初始化数据库规范

1 概念

服务程序入口主循环启动前执行数据库连接,不可放入循环内部,避免重复创建连接造成资源泄漏。

2 特性 / 规则

  1. 连接初始化放在监听 Socket 之前;

  2. 连接失败打印日志,程序可选择退出或重试;

  3. 禁止将 Connect 写入 while 服务循环,循环会反复创建连接;

  4. 插件版本不匹配会导致连接闪退,优先选用稳定 8.0 系列版本,禁用最新版。

3 语法 & 代码片段

static void Main(string[] args){ // 程序启动先初始化数据库 MySqlManager.Instance.Connect(); // 再开启Socket监听、主循环 StartServerSocket(); while(true){ ServerMainLoop(); } }

4 相关知识点对比

启动前初始化数据库 VS 循环内连接数据库

启动前一次连接:全局复用连接,无重复创建资源; 循环内连接:每秒新建连接,数据库瞬间爆满闪退。

5 模块拓展

数据库连接池优化,复用连接减少频繁创建销毁开销。

六、MySql 插件版本坑与环境处理

1 概念

MySql 官方 NuGet 最新版存在兼容性 Bug,和.Net 框架、其他第三方库冲突,开发固定使用 8.0 稳定分支,避免闪退、查询无数据问题。

2 特性 / 规则

  1. 禁止安装最新 Release 版本,优先选择 8.0.17、8.0.19 中间稳定版;

  2. 版本不匹配表现:数据库能连接但查询返回空、程序无报错直接闪退;

  3. 报错解决方案:卸载当前版本,重装 8.x 稳定版重启工程;

  4. 团队统一插件版本,防止本地正常线上报错。

3 相关知识点对比

最新版插件 VS 稳定 8.x 版本

最新版:API 频繁改动,依赖不兼容,联调大量未知 Bug; 稳定版:社区问题少,教程匹配,线上项目通用版本。

5 模块拓展

项目交付附带插件清单,统一 NuGet 版本号,新人一键还原环境。

七、登录注册 CS/SC 协议定义规范

1 概念

客户端发送 CSRegister/CSLogin 请求协议,服务端对应生成 SCRegisterResult/SCLoginResult 回执,两端协议类、字段序号完全一致。

2 特性 / 规则

  1. CS 类存储客户端上传参数(用户名、密码);

  2. SC 类存储业务枚举结果码,用于前端弹窗提示;

  3. [ProtoMember] 序号两端严格对齐,序号错乱解析失败;

  4. 协议直接复制同步客户端、服务端工程。

3 语法 & 代码片段

// 客户端注册请求 [ProtoContract] public class CSRegister : MessageBase{ public override string ProtoName => "CSRegister"; [ProtoMember(1)] public string UserName; [ProtoMember(2)] public string Password; } // 服务注册回执 [ProtoContract] public class SCRegisterResult : MessageBase{ public override string ProtoName => "SCRegisterResult"; [ProtoMember(1)] public RegisterResult Res; }

4 相关知识点对比

枚举返回结果码 VS 字符串提示信息

枚举数字:前后端统一标准,无文字拼写差异,分支判断简洁; 字符串:多语言、联调极易出现文本不一致 Bug。

5 模块拓展

新增注册验证码、渠道标识协议字段。

八、服务端消息分发处理逻辑

1 概念

收到客户端 CS 协议后转换实体,调用 UserManager 对应接口获取结果,组装 SC 回执消息异步下发至对应客户端 Socket。

2 特性 / 规则

  1. 协议名匹配分发,区分注册、登录两类业务;

  2. 数据库操作异常统一返回失败枚举;

  3. 下发回执复用原有客户端 Socket 通道;

  4. 分发逻辑与数据库操作分层隔离,底层网络不掺杂业务。

3 语法 & 代码片段

void HandleCSRegister(MessageBase msg, Socket clientSock){ CSRegister req = msg as CSRegister; RegisterResult res = UserManager.Instance.Register(req.UserName, req.Password); SCRegisterResult ret = new SCRegisterResult(); ret.Res = res; NetSendTool.Send(clientSock, ret); }

4 相关知识点对比

硬编码 if 分发 VS 反射 / 委托分发

少量业务 if 分支简单; 大量协议采用委托字典分发,新增业务无需修改分发代码。

5 模块拓展

分发时增加日志打印,记录账号注册、登录行为便于运维排查。

九、客户端消息回调与 UI 交互

1 概念

客户端发送账号密码后注册协议回调,收到服务回执根据枚举结果弹出对应弹窗提示(注册成功 / 账号已存在 / 密码错误)。

2 特性 / 规则

  1. 发送消息时绑定 Action 委托回调接收返回结果;

  2. 回调在 Unity 主线程 Update 处理,避免子线程修改 UI 报错;

  3. 不同结果码区分弹窗文字、按钮交互逻辑。

3 语法 & 代码片段

// 客户端发送注册并绑定回调 public void SendRegister(string name, string pwd, Action<RegisterResult> callback){ CSRegister msg = new CSRegister(); msg.UserName = name; msg.Password = pwd; NetManager.Instance.RegisterProto("SCRegisterResult", (resMsg) =>{ SCRegisterResult ret = resMsg as SCRegisterResult; callback(ret.Res); }); NetManager.Instance.SendMessage(msg); } // UI按钮调用 Btn.onClick.Add(()=>{ SendRegister(inputName.text, inputPwd.text, (res)=>{ switch(res){ case RegisterResult.Success: ShowTip("注册成功"); break; case RegisterResult.AccountExist: ShowTip("账号已存在"); break; } }); });

4 相关知识点对比

全局消息池统一处理 VS 发送时绑定独立回调

全局池:所有消息统一处理,分支臃肿; 独立回调:每条请求对应专属处理逻辑,代码隔离清晰。

5 模块拓展

登录成功自动跳转游戏主场景,失败清空输入框。

综合拓展

1 完整登录注册前后端业务全流程

服务启动先初始化 MySql 数据库连接;

客户端 UI 输入账号密码,点击按钮发送 CS 协议;

服务端接收协议,调用 UserManager 查询 / 插入数据库;

根据数据库返回结果组装 SC 回执下发;

客户端主线程回调,根据枚举弹窗提示;

注册成功数据库新增玩家行,登录更新登录时间字段。

2 高频 Bug 排查清单

数据库查询无数据:MySql 版本过高、实体字段名与表不匹配;

注册重复不提示:Count 查询条件写错;

协议解析失败:两端 ProtoMember 序号不一致;

服务启动闪退:数据库连接放在循环内、插件版本冲突;

UI 弹窗无响应:回调在子线程未抛至 Update 主线程。

3 面试核心考点

ORM 框架使用规范、数据库初始化时机、MySql 版本兼容性坑、登录注册完整业务链路、CS/SC 协议分层设计、委托回调实现客户端业务响应。

4 进阶拓展学习方向

数据库连接池、密码 MD5 加密存储、玩家背包数据表、断线重连后自动登录、多渠道第三方登录接入。

5 实战落地要求

独立搭建可完整联调前后端工程,实现注册、登录功能,写入简历前后端一体化游戏项目。