ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Droiyan目录服务器C++源码实战:VS2012编译、ODBC配置与部署避坑

Droiyan目录服务器C++源码实战:VS2012编译、ODBC配置与部署避坑 简介一份面向C/VC开发者的Droiyan Online服务端源码资源对应2012年版本项目围绕目录管理BadukDir、消息通信、数据库操作、错误日志记录与服务主程序等模块展开适合希望了解在线应用后台架构或Visual Studio 2012工程组织方式的读者。压缩包共86个文件大小约79.89MB以cpp/h源代码、obj中间文件、tlog构建日志为主同时包含sln/vcxproj工程文件、exe可执行程序、res资源、ipch预编译缓存与少量png图片既能直接阅读核心逻辑也能对照编译产物和升级报告理解工程构建流程。资源已吸引428人学习浏览属于模块划分清晰的旧版在线服务端代码。从中可以获取服务启动停止、记录集处理、消息机制封装、错误日志上报、数据库连接等典型实现对排查VC工程升级问题、梳理目录服务器通信流程以及开展C网络服务开发练习都有实际参考价值。1. 一眼看懂这个 2012 年的 Droiyan 目录服务器工程如果你手头有一个压缩包叫 v2012_Dir_droiyanOnline_droiyan_neo_droiyan开发_里面躺着一个 VS2012 就能打开的 C 服务端工程那它多半就是 Droiyan Online 的目录服务器Dir Server源代码。简单说这类服务器在在线服务架构里负责「引导」客户端启动后先连它它负责校验版本、分配区服入口、再把会话转发到真正的逻辑服。这份资源值不值得下载取决于你想干什么——想完整跑起一个 2012 年的服务端或者想研究老式 MFC ODBC Windows 服务的工程结构它都足够典型想直接拿到一个能上线运营的现代框架那它只能当参考。适合人群C 服务端入门、维护老项目、研究在线服务早期架构的开发者。2. 源码结构梳理40 多个文件里真正要看的 7 个模块先别急着双击.sln。这个压缩包混着源码、编译中间产物.obj、.pch、.tlog、IDE 缓存.sdf、.ipch、.suo和升级报告UpgradeLog.htm直接解压后目录很乱。我的习惯是先按扩展名把文件分类挑出真正的源码再按模块读。这一步花不了五分钟但能让你少走很多弯路——尤其是当你准备把这份代码当教材读的时候先建立全局视图比逐行读代码重要得多。2.1 文件清单源码、工程文件、垃圾文件先分三堆把压缩包内容按类型拆开核心其实只有三部分。我整理了一张清单你解压后可以照着分类别文件说明主程序源码BadukDir.cpp / BadukDir.h程序入口含 WinMain 或 main 前的初始化逻辑服务与网络ServiceMain.cpp / Net.cppWindows 服务生命周期、socket 监听与收发包数据库Database.cpp / Recordset.cpp / Recordset.hODBC 连接、CRecordset 派生类的数据读写消息与业务Msg.cpp / Msg.cpp.bak / SessDesc.cpp消息分发、会话描述、协议处理辅助模块ErrorLog.cpp / Userset.cpp / Userset.h / BadukDirCom.cpp日志、用户配置、COM 接口封装工程配置Dir.sln / BadukDir.vcxproj / BadukDir.dsp / BadukDir.dswVS2012 解决方案注意 .dsp/.dsw 是更老的 VC6 工程文件配置文件dir.ini运行参数端口、数据库 DSN 等可直接忽略*.obj / *.pch / *.tlog / *.sdf / *.ipch / *.suo / Debug / Release / ipch / res / Backup / _UpgradeReport_Files编译中间产物和 IDE 缓存不是源码这里有个值得注意的细节工程里同时存在.dsp/.dswVC6 时代的工程格式和.vcxprojVS2012 格式还有UpgradeLog.htm。这说明你手里这份代码不是原始版本而是被某个人用 VS2012 从老工程升级过的。所以如果你在后续编译时看到一些工程文件版本不一致的怪问题不用惊讶——这份资源本来就是一次升级操作的产物残留的升级报告文件就是证据。.obj、.pch、.tlog这类文件在解压包里会占不少体积但它们对阅读代码毫无帮助。.sdf和.ipch是 VS 的 IntelliSense 缓存删掉完全不影响编译它们会在你打开工程时重新生成。建议你解压后先把这些文件清理掉只保留源码和工程配置目录会清爽很多。2.2 七个核心模块各自干了什么读这份代码我建议按「服务入口 → 网络 → 数据库 → 业务」这条线往下走而不是从上到下逐个文件看。下面是我梳理的模块分工和你直接读源码得到的结论应该能对上。ServiceMain.cpp —— 服务的命脉。Droiyan 目录服务器是设计成 Windows 服务跑的ServiceMain 是服务的入口函数。它负责注册服务回调、初始化各子系统、进入消息循环。调试时你多半不会直接以服务方式调它而是绕过去走普通控制台启动这点后面部署章节细说。Net.cpp —— 客户端的门卫。目录服务器要同时面对大量客户端连接Net.cpp 管的就是 socket 的创建、bind、listen、accept 和收发包。2012 年这种规模的服务端用 Winsock select 模型是常见做法每个连接一个线程的写法也有但资源消耗大一般不会这么干。你在 Net.cpp 里大概率能看到对端口和最大连接数的控制逻辑这两个参数在 dir.ini 里会对应出现。Database.cpp / Recordset.cpp —— 数据存取层。Recordset 这个类名是 MFC 里 CRecordset 的标准命名习惯所以这个工程用的是 ODBC 数据源而不是后来流行的 ADO 或直接 MySQL API。Database.cpp 负责建立连接、执行 SQLRecordset.cpp 负责把查询结果映射成 C 结构体。这意味着部署时你必须先准备好 ODBC 数据源否则程序一启动就会在数据库初始化这一步失败。Msg.cpp —— 业务逻辑集中地。客户端连上来之后发什么协议、服务器回什么包都在这里处理。.bak后缀说明有人改过这个文件改之前留了备份——这是老一代程序员的好习惯如果你打算改这份代码也建议照做。ErrorLog.cpp —— 排查问题时的第一现场。这个模块把运行错误写进日志文件时间戳、错误码、出错位置是标准三件套。后面排错章节我会详细讲怎么利用它现在你只需要知道这份代码的日志写得还算规矩遇到问题先看日志不要一上来就断点调试。BadukDirCom.cpp —— COM 封装。这个文件的存在说明工程不只是单纯的服务程序还向外暴露了 COM 接口方便其他组件比如管理工具、监视面板调用目录服务器的内部功能。读的时候可以往后放它不影响主流程理解。Userset.cpp / SessDesc.cpp —— 用户配置与会话描述。这两个文件负责用户信息的读写和会话描述结构的维护。SessDesc 在目录服务器里很关键因为它的本质是「记录谁被分配到了哪个区服」这正好是 Dir Server 的核心职责——给客户端指路。2.3 数据怎么流从客户端连接到区服分配把这七个模块串起来整个目录服务器的工作流程就很清晰了客户端连接 → Net.cpp 建立 socket 会话 → Msg.cpp 解析请求协议 → Database.cpp / Recordset.cpp 查询用户状态、区服状态 → SessDesc.cpp 生成会话描述决定把客户端分配到哪里 → 通过 Net.cpp 把分配结果返回客户端 → ErrorLog.cpp 记录整个过程这里最核心的设计思想是「目录服务器不承载业务逻辑」。它只做三件事确认客户端身份、查一下哪些区服在线、告诉客户端该去哪。真正的游戏逻辑、战斗计算全在后端的业务服里目录服务器干的是分发和导流的活。理解了这个分层你就明白为什么这份代码的网络层和数据库层代码量不大而 Msg.cpp 和 SessDesc.cpp 才是阅读重点——协议和会话管理才是这类服务器的灵魂。3. 用 VS2012 编译成可运行服务工程配置与四步构建读代码是第一步真正把这份 2012 年的工程跑起来才是硬仗。这里最大的风险不是代码本身而是你的编译环境。先明确一个前提这份工程在 VS2012即 Visual C 2012平台工具集 v110下编译是最稳的用新版 VS 打开大概率会碰到工具集不兼容的问题具体现象和解决办法见第 5 章。如果你手头恰好有 VS2012那整个编译过程会非常顺利。3.1 环境准备除了 VS2012还要装什么组件VS2012 安装时默认不会装全所有组件。这个工程用的是 MFC 和 ODBC所以你在安装 VS2012 时必须勾选「MFC 和 ATL 支持」相关组件否则编译到一半会报cannot open include file afxdb.h——这个头文件是 MFC 数据库类CRecordset 那套的核心缺失它会让你误以为代码写错了实际是环境问题。操作系统方面Windows 7 / 10 / 11 都能跑 VS2012但 Win10 以上系统建议把 VS2012 的更新补丁打全否则偶发崩溃比较烦人。另外工程文件里有.vcxproj说明它已经被升级到 VS2012 格式直接双击Dir.sln就能打开不需要再做格式转换。3.2 编译操作命令行和 IDE 两种方式打开Dir.sln后你会看到整个解决方案里就一个项目BadukDir。在解决方案管理器里右键项目 → 属性先确认几个关键配置平台工具集选Visual Studio 2012 (v110)字符集选「使用多字节字符集」配置管理器里把活动解决方案平台设为Win32。注意「使用 Unicode 字符集」必须改掉很多老工程在 VS2012 里默认被切成 Unicode会导致一大片类型不匹配的报错。IDE 方式操作简单菜单栏「生成 → 重新生成解决方案」然后看输出窗口。如果你偏好命令行也可以用 VS2012 自带的开发者命令提示符直接执行# 用 VS2012 开发者命令提示符执行/Rebuild 表示全量重建 devenv Dir.sln /Rebuild Release|Win32这里Release|Win32是解决方案配置名devenv是 VS 自带的命令行编译工具。如果你在普通 CMD 里执行会提示找不到命令需要先调用vcvarsall.bat初始化环境变量或者直接用「VS2012 开发人员命令提示」快捷方式打开。编译成功后在Release目录下会生成Dir.exe。如果这一步报错不要急着改代码——先看错误信息指向的是哪个文件。这个工程里最容易翻车的报错我放在第 5 章的避坑清单里每条都有对应解法。3.3 编译输出什么可执行文件与配套文件的对应关系编译完成后你会得到Dir.exe但只有这个 exe 跑不起来。你需要确认 Release 目录下同时存在以下文件Release/ ├── Dir.exe # 主程序 ├── dir.ini # 配置文件编译前就要改好参数 ├── BadukDir.ini # 如果存在会被程序优先读取 └── *.dll # 如果有依赖的动态库需要一并拷贝dir.ini是这套程序能不能起来的关键。它不参与编译是纯运行时配置程序启动时会去当前工作目录找它。具体每个参数怎么设下一章展开讲。你现在只需要记住编译只是第一步「编译通过」和「能跑起来」完全是两回事中间隔着配置文件和服务注册。3.4 编译日志与常见编译警告的解读编译过程中的警告信息值得扫一眼。老工程在 VS2012 下编译经常出现 C4996 警告关于strcpy等不安全函数和 C4244 警告类型转换可能丢失数据。这些通常不影响运行但如果你打算把代码改到新环境跑就要认真对待。C4996 可以通过在stdafx.h顶部加#define _CRT_SECURE_NO_WARNINGS压制C4244 则需要逐个看确认是安全的窄化转换还是有隐患的截断。我自己编译这种老工程时有个习惯先记录干净的编译结果警告数量改完代码再对比。如果警告数突然变多往往说明改动引入了新问题。编译警告不是玄学它是在替你提前发现潜在的运行时毛病。4. 部署与联调数据库配置、ini 参数和服务注册编译通过只是拿到了一个哑巴程序真正让它干活还需要完成三件事配好数据库、改对 ini 参数、把进程注册成 Windows 服务。这一章把这三步拆开每步都给你可以直接抄的配置。4.1 数据库准备ODBC 数据源是绕不过去的坎前面提到工程里是CRecordset这套 MFC ODBC 类所以程序本身不直接管连接串而是通过 ODBC 数据源DSN找数据库。这意味着你必须在操作系统里先建好一个系统 DSN程序才能通过名字连上库。创建步骤控制面板 → 管理工具 → ODBC 数据源64 位进入「系统 DSN」选项卡点击添加选择 SQL Server或者你用的数据库对应的驱动填写数据源名称。这里有个关键提醒如果 Dir.exe 是 32 位程序这个工程默认就是 Win32 编译64 位 Windows 上必须用 32 位 ODBC 管理器来创建 DSN。32 位 ODBC 管理器的路径是C:\Windows\SysWOW64\odbcad32.exe你用控制面板打开的往往是 64 位版本两者创建的 DSN 不通用程序会报「找不到数据源名」。在Database.cpp或Recordset.cpp里你大概能看到类似这样的连接逻辑// Recordset.cpp 中的典型 ODBC 打开方式 CRecordset rs(g_db); rs.Open(CRecordset::snapshot, _T(SELECT * FROM ServerList));这里的g_dbCDatabase 对象就是通过 DSN 名建立的连接。DSN 名和 ini 里的配置必须一字不差包括大小写。如果你不知道 DSN 应该叫什么先读Database.cpp里的CDatabase::Open调用第一个参数就是 DSN 名照抄到 ini 里即可。4.2 dir.ini 参数说明端口、DSN、会话上限一口气配齐dir.ini是这份资源的运行时黑匣子开关程序启动时的行为基本都由它决定。常见配置项大概是这样的结构[Server] Port8100 MaxSession512 [Database] DSNDroiyanDir UIDsa PWDyour_password [Log] LogLevel2 LogFileErrorLog.txt各参数含义参数可能的值说明Port8100 等未占用端口目录服务器监听的端口必须和客户端配置一致MaxSession512 / 1024最大并发会话数调太大会吃内存太小客户端会被拒DSNDroiyanDir 等对应数据库里的系统 DSN 名称UID/PWD数据库账号密码如果数据库用 Windows 身份验证这两个可以留空LogLevel0~3日志详细程度排查问题时调到最高LogFile任意文件名日志输出位置建议用绝对路径调整端口的场景很常见比如 8100 被占用。改 ini 是最省事的办法改完重启进程生效。但注意MaxSession不是越大越好——每个会话都会占用内存和 socket 句柄2012 年的服务端设计通常偏保守512 到 1024 是常见区间你要根据自己的机器内存实测不要一把梭哈调到 65535。4.3 两种启动方式控制台调试与服务注册调试阶段推荐先用控制台方式跑。很多这种老工程会在代码里做判断如果带某个命令行参数比如-console就走控制台逻辑而不是启动 Windows 服务。你可以直接在 Release 目录下打开 CMD 执行# 前台运行日志直接打到控制台方便观察 cd C:\Droiyan\Release Dir.exe -console前台运行的好处是你能直接看到打印输出CtrlC 就能停掉不用每次去服务管理器里点。如果程序没有实现这个参数你也可以暂时改一下ServiceMain.cpp的入口逻辑把服务注册的部分跳过去直接调初始化函数——但这样每次编译都改代码比较麻烦我一般先试-console不行再动代码。正式部署再注册成服务。确认程序在前台模式下能正常启动、数据库连接成功、端口正常监听后就可以把它做成 Windows 服务了:: 以管理员身份运行 CMD创建并启动服务 sc create DroiyanDir binPath C:\Droiyan\Release\Dir.exe start auto sc start DroiyanDir sc query DroiyanDirsc create的三个参数要特别留意binPath后面必须有一个空格再接路径start同理空格丢了会报参数错误。sc query用来确认服务状态返回RUNNING就说明服务已经跑起来了。如果你改过 ini 或换了路径需要先sc stop DroiyanDir再sc start DroiyanDir重启服务。4.4 联调自检三步确认目录服务器真的在工作服务启动后别急着接客户端先做三个验证:: 第一步确认进程活着 tasklist | findstr Dir.exe :: 第二步确认端口在监听把 8100 换成你的配置值 netstat -ano | findstr :8100 :: 第三步手动发一个空包测试 socket 是否响应 telnet 127.0.0.1 8100netstat输出里能看到监听状态LISTENING和对应的 PID。如果你看到的是TIME_WAIT而没有LISTENING说明进程没起来或者端口配置没生效。telnet能连上至少证明网络层通了如果直接拒绝连接回头看 ErrorLog 输出基本能定位是端口被占还是服务没初始化完。血泪经验服务方式运行时的工作目录和你手动执行Dir.exe -console时不一样。Windows 服务默认的工作目录是System32如果你的 ini 用的是相对路径服务模式可能找不到配置文件而启动失败但控制台模式却一切正常。解决方法是 ini 路径都用绝对路径或者注册服务时把工作目录信息写进程序逻辑里。这个问题极其隐蔽我第一次部署时花了两个小时才排查出来。5. 避坑清单编译、运行、部署的 5 条踩坑记录这一章把我反复见到的坑集中写出来。每一条都是「现象 → 原因 → 解决」三段式你照着对照就行。这些坑不是猜的是这类老工程在 Win7/10/11 上移植时几乎必然踩中的点。5.1 用新版 Visual Studio 打开工程直接 MSB8020 报错现象双击Dir.slnVS2019/2022 弹出错误MSB8020: 未找到 Visual Studio 2012 (v110) 工具集生成直接失败。原因新版 VS 默认不带 v110 平台工具集而工程文件里明确指定了 v110。VS 试图用 v110 编译但机器上没装。解决两条路。最省事的是直接用 VS2012 编译环境完全匹配。如果你坚持用新版 VS可以在项目属性 → 配置属性 → 常规 → 平台工具集里把它改成Visual Studio 2019 (v142)或对应版本然后处理后面会冒出来的兼容性问题。不过说实话对于只是想跑通和读代码的人来说装 VS2012 是性价比最高的选择。5.2 编译报错 cannot open include file afxdb.h现象编译到Database.cpp或Recordset.cpp时报fatal error C1083: Cannot open include file: afxdb.h: No such file or directory。原因afxdb.h是 MFC 数据库扩展头文件只在安装了「MFC 和 ATL 支持」组件的 VS 里存在。如果安装 VS 时没勾选这个组件无论你怎么改代码都没用。解决打开 VS 安装器勾选「使用 C 的桌面开发」工作负载下的「适用于最新 vXXX 生成工具的 C MFC」可选组件安装完重启 VS 重新编译。5.3 数据库连接报错找不到数据源名且没有默认驱动程序现象程序启动后 ErrorLog 里出现State: IM002, 未找到数据源名称且未指定默认驱动程序或直接弹 ODBC 错误对话框。原因99% 的情况是 DSN 没建对。要么没创建 DSN要么创建时用的是 64 位 ODBC 管理器而程序是 32 位或者 DSN 名与 ini/代码里不一致。解决打开 32 位 ODBC 管理器C:\Windows\SysWOW64\odbcad32.exe确认系统 DSN 里存在dir.ini中填写的名称并先用 ODBC 管理器里的「测试连接」按钮验证数据库能通。测试通过再启动程序基本一次过。5.4 端口被占用导致网络模块初始化失败现象前台运行或服务启动后日志里出现bind failed: 10048或者bind: Address already in use程序随即退出。原因10048 是 Winsock 错误码意思是端口已被其他进程占用。常见情况是上次的程序没退干净或者端口被 IIS、其他服务占用了。解决先用netstat -ano | findstr :8100换成你的端口找到占用进程的 PID再去任务管理器确认是哪个进程。确认无主后要么杀掉占用进程要么改dir.ini里的端口换一个重启程序。如果换了端口还报同样的错检查是不是代码里把端口写死了比如直接在Net.cpp里 hardcode 了 8100这种情况要改代码而不是改 ini。5.5 服务启动立即退出且日志无输出现象sc start DroiyanDir提示启动成功但两三秒后服务状态变成已停止ErrorLog 文件也没生成或为空。原因Windows 服务启动时工作目录通常是C:\Windows\System32而 ini 和数据文件可能用的是相对路径服务进程根本找不到配置文件在初始化早期就直接退出了。日志没输出是因为它还没来得及走到初始化日志模块。解决两条手路。第一把dir.ini和所需数据文件全部改成绝对路径。第二在ServiceMain.cpp的入口处先SetCurrentDirectory到程序所在目录// ServiceMain.cpp 里服务入口最前面加这两行 TCHAR szPath[MAX_PATH]; GetModuleFileName(NULL, szPath, MAX_PATH); PathRemoveFileSpec(szPath); // 取出 exe 所在目录 SetCurrentDirectory(szPath); // 把工作目录切过去这段代码的思路是先拿到Dir.exe的完整路径去掉文件名部分得到目录再把当前工作目录切到那里。这样不管服务以什么方式启动都能在同一目录下找到 ini 和日志文件。从那以后我每次接手老服务工程都强制先搜一遍代码里有没有.ini或相对路径的硬编码有就先改掉避免中途翻车。6. 进阶老工程迁移到新版 VS 的思路与调试技巧如果你已经用 VS2012 把这份 droiyan 目录服务器跑通了下一步大概率会想能不能把它迁移到新版 VS顺手加上更好的调试手段。这一章讲两个实用方向迁移思路和一个日志调试技巧。6.1 迁移到 VS2019/2022 的三个关键改动点平台工具集从 v110 换成 v143 只是开始真正耗时的是下面三处第一字符集切换。老工程多半用多字节字符集新 VS 默认 Unicode。把项目属性里字符集改为多字节是保底做法但更推荐逐步把char*替换成TCHAR或LPCWSTR工程大了以后字符集一致能少很多隐患。第二MFC 和 CRT 函数替换。C4996警告在新 VS 里是编译错误级别strcpy、sprintf这些不安全函数全得换成_s后缀的版本。第三ODBC 驱动兼容。新版 Windows 自带的 ODBC 驱动基本能兼容老代码但如果你连的是旧版 SQL Server可能要安装对应的 ODBC Driver 并修改 DSN 配置。迁移本身不复杂但每一步都会冒出一堆编译错误。我的建议是先把工具集换掉让工程能编译再按错误列表一个一个消不要试图一次把所有问题处理完。6.2 调试技巧让老工程输出 DebugView 日志老工程日志都写文件边跑边 tail 文件有点累。一个高性价比的做法是给 ErrorLog.cpp 加一行输出到 OutputDebugString// ErrorLog.cpp 里写日志的函数中追加 void WriteLog(LPCTSTR msg, int level) { // 原有文件写入逻辑保留 // 追加同时输出到调试器方便用 DebugView 实时观察 OutputDebugString(msg); OutputDebugString(_T(\n)); }OutputDebugString是 Win32 API会把字符串发给调试器或 DebugView 这类工具。配合 Sysinternals 的 DebugView 小工具不需要挂 VS 调试器就能实时看到日志输出而且不影响服务正常运行。对于排查服务启动即退出这类问题比翻日志文件高效得多。一个更小巧的用法是临时在代码里夹带输出// 排查某个函数的入参临时加一行定位完再删 OutputDebugString(_T(Net.cpp: OnAccept called, session_id));这种做法在接手不熟悉的代码时非常管用——你不需要理解整个模块就能定位问题。记得用完删掉别把调试代码留在正式逻辑里。我自己的习惯是每次改动 ErrorLog 相关代码后都会强制跑一遍「控制台启动 → 连一个模拟客户端 → 看 DebugView 输出」的流程确认日志链路没被改坏再继续下一步。迁移这种老工程最怕的不是代码不懂而是你改完之后不知道它到底坏没坏有个实时日志通道心里就踏实了。希望帮到你。本文还有配套的精品资源点击获取
返回列表