资讯动态

ConquestDICOMServer 搭建 DICOM 测试基站:从 C-ECHO 到 C-GET 闭环

发布时间:2026/9/26 6:48:07 来源:尧图企业网站定制
简介面向医疗影像开发与系统集成人员的DICOM测试工具包基于开源的Conquest DICOM Server构建以DICOM SCP角色运行可接收处理影像存储请求并支持worklist工作列表查询用于验证PACS设备间通信是否符合标准。压缩包约22.28MB内含批处理配置脚本、DICOM字典、核心动态库、主服务程序及匿名化脚本覆盖服务端安装、自动更新、控制台监控与数据脱敏等操作。已有716人学习下载适合需要快速搭建本地DICOM服务环境、开展设备兼容性测试或性能评估的开发者与管理员。借助工具可一键启动服务端点通过控制台脚本观察日志结合匿名化脚本保护患者隐私并利用网关组件实现跨设备转发从而在接入新影像设备前充分验证交互正确性。对于医疗信息化、PACS集成和DICOM协议开发人员该资源能缩短环境搭建时间提供可直接复用的服务端配置与调试方案降低集成验证门槛。1. 为什么拿 ConquestDICOMServer 当 DICOM 测试工具先搞清它的定位做 PACS 集成的工程师都经历过这种尴尬对方系统号称支持 DICOM 协议联调时 C-ECHO 却怎么都不通查了一圈最后发现是对方的 AETitle 多了一个空格。这种问题靠大厂 PACS 去复现成本高、起停慢、日志还不透明真正顺手的反而是 ConquestDICOMServer 这种轻量级服务器。它不是一个给你写用例的“软件测试工具”而是一台能扛住真实 C-STORE、C-FIND、C-GET 请求的 DICOM 基站把它摆在本地当 DICOM 协议栈的“接口测试工具”来用省下的时间远比研究它的时间多。Conquest 是开源免费的 DICOM 服务器一个可执行文件加一个配置文件就能跑起来默认用 SQLite 当影像索引库支持 Storage、Query/Retrieve、WADO 等核心服务。适合三类人做 PACS/ RIS 对接的开发者、需要反复验证 DICOM 通信链路的测试工程师、带学生做医学影像实验的老师。这篇就从零把它搭起来再拿 DICOM 工具跑通存储、查询、拉取闭环最后讲几个我踩过的坑。2. 装好一台能当测试基站的 Conquestdicom.ini 与数据库选型2.1 先认识它的组成一个 exe 加一个 ini 就是整套服务器Conquest 的 Windows 版核心程序叫 dgate.exeLinux 下编译出来的可执行文件也叫 dgate。它不像 Orthanc 那样有 Web 后台、插件体系、多容器编排也不像 dcm4chee 那样需要 JBoss 部署。它就是把 DICOM 的四个核心服务Storage SCP、Query/Retrieve SCP、WADO、Worklist全部塞进一个进程靠读取同目录下的 dicom.ini 来决定行为。dgate.exe dicom.ini dicom.db SQLite 数据库有的版本放在 data 子目录 dgate.log 运行日志排错主要看它dicom.ini 是唯一配置入口数据库类型、端口、AETitle、目录、超时全在这里。对于测试环境我一般直接把 SQLite 作为默认存储不需要额外部署 MySQL。SQLite 单文件数据库的好处是“后悔药”特别多——出问题时直接备份 dicom.db 文件或者删掉重建十几秒就能回到干净状态这在反复灌测试数据的场景下太重要了。提示Conquest 也支持 MySQL、PostgreSQL 和 Oracle但那是生产环境才需要考虑的选型。测试用的基站SQLite 单文件足够还免维护。2.2 安装与启动Windows 服务模式和 Linux 前台模式Windows 下安装 Conquest 后程序目录里会有一个 dicom.ini 和 dgate.exe。以管理员身份打开命令行用 dgate -i 把服务注册成 Windows 服务这样开机自启、崩溃后由服务管理器拉起。调试阶段我更喜欢用 dgate -d 前台运行日志直接打到终端上按 CtrlC 就能停。dgate -i # 安装为 Windows 服务 dgate -u # 卸载服务 dgate -r # 重新读取 dicom.ini改完配置不用重启服务 dgate -d # 前台调试模式运行日志实时输出Linux 下没有服务注册的概念常见做法是用 systemd 写个 unit 文件或者直接 nohup 启动。我一般先用前台模式验证配置没问题再交给 systemd 托管cd /opt/conquest ./dgate -d # 前台跑确认端口起来后 CtrlC 停掉 nohup ./dgate dgate.log 21 # 正式跑启动后立刻验证端口是否监听。Conquest 默认 TCP 端口是 5678验证命令netstat -an | grep 5678看到 LISTEN 状态就说明进程起来了。如果端口没监听多数是 dicom.ini 配置格式错误或者 5678 被别的程序占用翻 dgate.log 基本能看到原因。2.3 dicom.ini 里的七个必调参数dicom.ini 的格式不是标准 INI 那么规整不同版本字段略有差异但下面这些是测试环境的“必调清单”。我以最常用的几个字段为例给出一份能直接照抄的配置片段[sss] ServerName CONQUEST TCPPort 5678 SQLite dicom.db DatabaseType sqlite MaxPatients 100000 MaxPDU 16384 SSSCPTimeout 30ServerName这个值就是 DICOM 的 AETitle客户端连接时填的就是它。DEBUG 阶段我习惯把它改成一眼能认出的名字比如TEST_QACS避免和别的服务器混淆。TCPPort监听端口默认 5678。如果 5678 被占用或想模拟多服务器环境改成 5679、5680 都行。SQLite / DatabaseType指定 SQLite 数据库文件名和类型。测试环境不用动。MaxPatients最大病人记录数默认 9 万多灌大批量测试数据时如果报记录数超限就调大这个值。MaxPDU最大协议数据单元建议改成 16384 或 32768这样传输大图比如增强 CT 的 512×512×上千层时不容易因分片过多而卡顿。SSSCPTimeout关联超时单位秒。网络不好的测试环境可以调大到 60否则经常出现 C-STORE 中途断连。另外还有两个藏在后面段里的参数值得注意WorkList 0表示关闭 Worklist SCP如果测试需要 MWL改成 1 并配置 Worklist 端口EnableReadAhead 1表示开启预读缓存对频繁查询同一患者的场景速度提升明显。2.4 区分四个 DICOM 服务在 Conquest 里的定位Conquest 一个进程里同时跑着多个 DICOM 服务但它们不是“四个端口”而是共享同一个 TCP 端口靠 DICOM 协议里的 Presentation Context 来区分。搞清这一点后续测试才不会迷茫Storage SCP接收其他设备推送的 DICOM 文件C-STORE这是最常用的测试入口。Query/Retrieve SCP响应 C-FIND、C-MOVE、C-GET 请求客户端可以按患者、检查、序列条件检索影像。WADO基于 HTTP 的影像访问服务端口由 [www] 段的 WADOPort 参数决定默认一般是 8080。Worklist SCP默认关闭响应 MWL 查询给模态设备返回待检查列表。测试时C-ECHODICOM 的 ping是最先验证的它不涉及数据库操作只验证端口、AETitle、Presentation Context 是否匹配。C-ECHO 通了说明 TCP 层和 DICOM 关联层没问题再往下测 C-STORE 才有意义。这里也提醒一句C-ECHO 通不代表存储没问题它只是最基本的握手。3. 把 Conquest 当接口测试工具跑通第一个闭环echo、store、find、get3.1 先用 echoscu 验证 C-ECHO三秒钟判断服务器是否活着测试 DICOM 服务器最常用的客户端工具是 DCMTK 自带的命令行套件或者 dcm4che 的 Java 工具。DCMTK 编译好的二进制可以从官网下载Windows 和 Linux 都有。我习惯用 echoscu 先做关联测试echoscu -v -aet TESTSCU -aec CONQUEST 127.0.0.1 5678参数含义-aet TESTSCU是请求方的 AETitle-aec CONQUEST是目标服务器的 AETitle必须和 dicom.ini 里的 ServerName 一致IP 和端口跟在最后。返回EchoSCU: Association Accepted就说明 C-ECHO 成功。实际场景里最常见的问题是-aec填错——大小写不对、多了空格、或者根本不知道服务器的 AETitle 是什么导致卡在Association Rejected。遇到这种情况第一反应不是调客户端而是先看服务器日志里拒绝原因Conquest 的 dgate.log 会明确写清楚“Unknown AETitle”还是“Bad Presentation Context”。3.2 用 storescu 推一张 DICOM 文件并验证数据库落库C-ECHO 通了接着测最核心的 C-STORE。先准备一张 DICOM 文件比如test_ct.dcm用 storescu 推过去storescu -v -aet TESTSCU -aec CONQUEST 127.0.0.1 5678 test_ct.dcm正常会输出一行 C-STORE 响应比如Store SCP Result: Success (0x0000)。这时如果只看到客户端成功就急着认为服务器没问题那就漏掉了一半。Conquest 是带索引数据库的服务器文件写进磁盘只是第一步索引必须也落库否则后续查询会查不到。验证落库的方法有两种。第一种是看数据库记录Windows 下可以用 sqlite3 命令行工具直接查sqlite3 dicom.db SELECT PatientID, StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID FROM DICOM_IMAGES;这条命令会列出所有已存储影像的四个关键 UID。能看到刚发送的那条记录就说明 Storage SCP 和数据库写入都完成了。第二种方法是再用 findscu 查询一遍这正好是下一节的内容。3.3 用 findscu 做 C-FIND按患者 ID 把记录查回来存储能落库不代表查询链路没问题。DICOM 的 C-FIND 和数据库查询不是一回事它走的是 DICOM Query 模型需要服务器把请求翻译成 SQL 再去查库。我用 findscu 验证findscu -v -aet TESTSCU -aec CONQUEST 127.0.0.1 5678 -k QueryRetrieveLevelPATIENT -k PatientIDTEST001-k参数指定查询条件这里按患者 ID 查。返回结果里会列出所有 PatientID 匹配的记录包含 StudyDate、Modality、StudyDescription 等属性。如果 C-STORE 成功但 C-FIND 查不到多半是数据库索引没写进去或者 PatientID 根本不匹配——注意 DICOM 的 PatientID 是字符串精确匹配TEST001和test001是两条不同记录。这里有个容易混淆的地方C-FIND 的查询层级QueryRetrieveLevel分 PATIENT、STUDY、SERIES、IMAGE 四级。测试时要按层级逐级查先查 PATIENT 拿到 StudyInstanceUID再查 STUDY 拿 SeriesInstanceUID最后查 SERIES。跳过层级直接查 IMAGE很多服务器会直接返回SOPClass Not Supported或者空结果。3.4 用 getscu 拉图验证 C-GET和 C-MOVE 的差别要分清查询确认记录存在后还得验证能否把图像从服务器里拉回来。C-GET 和 C-MOVE 都能做 Retrieve但机制不同C-MOVE 是服务器作为 SCP 把图像主动推给第三方 AETitle需要一个“搬运工”目标C-GET 是服务器直接在当前连接上把图像发给请求方。对测试环境来说C-GET 更简单不需要事先配置接收端getscu -v -aet TESTSCU -aec CONQUEST 127.0.0.1 5678 -k QueryRetrieveLevelSERIES -k StudyInstanceUID1.2.3.4执行后当前目录会出现接收到的 DICOM 文件。拿到文件后我习惯用dcmdump检查 SOPInstanceUID 和原始文件是否一致再对比文件大小dcmdump received_0001.dcm | grep SOPInstanceUID ls -la received_0001.dcm如果文件大小有明显差异多半是传输过程中被截断。这时看 Conquest 日志里的C-GET条目里面会记录发送的字节数和客户端收到的做对比就能定位是服务器端发送不完整还是客户端接收有问题。这个“存储→查询→取回”的完整闭环是我每次搭好 Conquest 后必跑的验收项。4. 把 Conquest 当测试工具时最容易翻车的 5 个坑现象、原因、解法4.1 端口被占导致 dgate 起不来日志却啥也不写现象执行 dgate -d 后进程秒退dgate.log 里只有一行Starting DICOM Server就没了下文端口 5678 也没有 LISTEN。原因Conquest 对网络端口绑定的错误处理比较“闷”bind 失败时不往日志里写详细错误直接退出。常见原因是本机已经跑着一个旧版 Conquest或者 5678 被其她服务占了。解决先查端口占用再决定是换端口还是杀进程。netstat -ano | grep 5678 # Windows 下最后一列是 PIDLinux 下是进程名如果确认是被占我一般优先改dicom.ini里的TCPPort到 5679而不是去杀别人的进程这样更稳妥也方便日后同时跑多个测试基站。4.2 AETitle 匹配不上C-ECHO 一直被拒现象echoscu 执行后返回Association Rejected: Called AE Title Not Recognized但端口明明是通的。原因客户端-aec填的 AETitle 和服务器dicom.ini里ServerName不一致。这个坑看似简单但在实际联调里发生率极高——对方给的 AETitle 写在文档里文档和真实配置隔了一版或者 AETitle 里带了不可见空格。解决先在服务器上确认 AETitlegrep -i ServerName dicom.ini然后严格按这个值去填-aec。同时要记住 AETitle 最长 16 个字符大小写敏感CONQUEST和Conquest会被当成两个不同的 AETitle。4.3 SQLite 被锁库C-STORE 报 “Database Locked”现象用多个客户端线程同时往 Conquest 推送 DICOM 文件随机出现 C-STORE 响应失败服务器日志里写database is locked。原因SQLite 在并发写入场景下对数据库文件加锁多个进程同时写同一张表就会锁冲突。这个问题在单客户端测试时几乎不出现一旦并发压测就暴露。解决测试环境有几个选择。第一把dicom.ini里DatabaseType换成sqlite并开启 WAL 模式第二限制并发推送数比如压测脚本里用 4 个并发而不是 16 个第三如果确实要模拟高并发写入把数据库切到 MySQLConquest 对 MySQL 的支持很成熟连接数不受单文件锁限制。对大多测试场景WAL 模式加上合理并发数已经够用。4.4 大文件传输超时C-STORE 中途断开现象推送单个体积超过 1GB 的多帧 DICOM 文件时客户端显示传输到一半就断开服务器日志出现Timeout或Connection reset by peer。原因默认的SSSCPTimeout只有 30 秒大文件在慢速网络上传输耗时超过这个阈值服务器就主动断连。加上默认MaxPDU较小数据分片多每片都要额外确认放大了耗时。解决把SSSCPTimeout调到 60 到 120 秒MaxPDU调到 32768。改完记得 dgate -r 重新载入配置。SSSCPTimeout 120 MaxPDU 32768注意SSSCPTimeout改大对并发接收有影响它表示每个关联的最大空闲时间超时调大后长期挂着的连接会占用资源。在纯测试环境里不用纠结但在压测时建议保持在 30 到 60 秒。4.5 中文患者姓名乱码查询和显示都受影响现象DICOM 文件里 PatientName 是中文storescu 推送到 Conquest 后用 findscu 查回来显示成乱码或者在数据库里看到一串无法辨认的编码。原因DICOM 默认字符集是 ASCII中文需要声明SpecificCharacterSet为GB18030或ISO_IR 192即 UTF-8。如果生成 DICOM 文件的工具没有正确写这个 TagConquest 按默认字符集解析中文就变成乱码。解决检查源文件字符集声明dcmdump test_ct.dcm | grep SpecificCharacterSet如果没有声明或声明不对用 DCMTK 的 dcmodify 补上dcmodify -i (0008,0005)GB18030 test_ct.dcm补完再推送。Conquest 查询端如果还乱码检查客户端 findscu 的输出编码部分终端默认用 UTF-8而服务器返回 GB18030 时显示会乱这时要把终端切到 GB18030 或让服务器统一存 UTF-8。5. 进阶验证用 WADO 和日志做自动化回归测试基站的稳定性不能靠一次手工测试下结论我把 Conquest 的验证分成三层协议层、接口层、业务层。协议层用 echoscu/storescu 已经覆盖接口层可以通过 WADO 来验证业务层靠日志和自动化脚本回归。WADO 是 Conquest 自带的 HTTP 接口适合做接口级验证。先在 dicom.ini 里确认 WADO 配置[www] WADOPort 8080 EnableWADO 1保存后重启 dgate用浏览器或 curl 访问curl http://127.0.0.1:8080/wado?requestTypeWADOstudyUID1.2.3.4seriesUID1.2.3.4.5objectUID1.2.3.4.5.6contentTypeapplication/dicom -o output.dcm返回的 output.dcm 可以直接用 dcmdump 验证能解析出正常 DICOM 头就说明 WADO 转发链路没被破坏。这里要注意Conquest 的老版本 WADO 路径是/wado新版本可能加了上下文如果 404 就查一下日志里请求路径和服务器注册的 servlet 路径。自动化回归我通常写一个简单的批处理脚本把 echoscu、storescu、findscu 串起来循环跑指定次数一旦某一步返回非零就退出并记录日志#!/bin/bash for i in $(seq 1 50); do storescu -aet TESTSCU -aec CONQUEST 127.0.0.1 5678 test_ct.dcm || exit 1 findscu -aet TESTSCU -aec CONQUEST 127.0.0.1 5678 -k QueryRetrieveLevelSTUDY -k PatientNameTEST -k StudyInstanceUID1.2.3.4 || exit 1 echo iteration $i passed done跑完后看 Conquest 的 dgate.log 里有没有ERROR级别的记录grep ERROR dgate.log | tail -20这轮 50 次循环里如果每个迭代都成功且日志无 ERROR基本可以判断这台 Conquest 作为测试基站的稳定性合格。整个过程不用人工盯着挂在持续集成里也一样跑。我自己的习惯是每次改完 dicom.ini 后先跑一次最小闭环echostorefindget再跑一次 30 分钟稳定性循环最后把数据库文件备份一份命名成带日期的版本。这样哪怕后面把配置改坏了也有后悔药可以吃不至于回到“啥都推不进服务器”的状态。希望这套流程对你有帮助。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑