ARTICLE DETAIL

资讯详情

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

Conquest DICOM Server测试实战:从启动失效到协议级故障定位

Conquest DICOM Server测试实战:从启动失效到协议级故障定位 简介这是一套面向医疗影像系统开发者、PACS集成工程师及DICOM协议学习者的Conquest DICOM Server测试工具包专用于模拟DICOM SCP服务、验证设备通信与工作列表Worklist功能。资源包含核心可执行文件ConquestDICOMServer.exe、网关服务DgateServ.exe、控制台启动脚本console.bat、匿名化脚本anonymize_script.cq及DICOM字典dgate.dic等关键组件辅以7za.exe压缩工具和自动化批处理脚本支持快速部署、日志监控与患者数据脱敏。压缩包共22.28MB含多个可执行程序、批处理文件、配置脚本与动态链接库覆盖服务器启动、更新、Web安装测试及协议解析等完整测试链路。目前已有718人学习下载使用者可直接运行环境开展DICOM图像接收存储、Worklist查询交互、网络连通性验证及合规性匿名化实践是医疗IT系统联调与教学实验的高实用性开源工具集。1. Conquest DICOM Server 测试工具不是装完就能跑的“黑匣子”而是影像科IT人员手里的听诊器你刚在 Windows 上双击ConquestServer.exe界面弹出绿色状态栏日志里刷着DICOM server started on port 104——恭喜服务“启动成功”了。但当你用 RadiAnt 或 OsiriX 发送一个 CT 序列服务器日志却卡在Received association request后再无下文或者 PACS 端发来 200 张图像Conquest 只存下前 37 张其余静默丢弃更常见的是dgate.exe进程 CPU 占用飙到 95%但dicom.log里连一条 ERROR 都没有。这不是玄学是 Conquest DICOM Server 作为一款轻量级、开源、跨平台的 DICOM 服务实现在真实临床环境尤其国产设备混用、老旧协议版本、非标准 SOP Class中暴露的典型“启动即失效”陷阱。它不提供 GUI 配置向导不校验 DICOM 文件结构合法性不自动重试失败传输也不告诉你哪张图因 Transfer Syntax 不匹配被悄悄跳过。本文聚焦Conquest DICOM Server 作为测试工具的核心用法如何用最小配置验证设备连通性、捕获真实网络流量、定位协议层断点、批量回放历史会话——所有操作均基于官方 v1.5.4a2023 年稳定版源码编译包无需修改 C 代码纯靠dicom.ini、命令行工具和日志解析闭环落地。适合影像科工程师、PACS 实施顾问、医疗 AI 数据工程师——你们要的不是“能跑”而是“知道哪里没跑、为什么没跑、怎么证明它跑了”。2. 从零构建可验证的 DICOM 测试环境用 dgate dcmdump 搭建最小闭环Conquest 的核心是dgate.exeWindows或dgateLinux/macOS它既是 DICOM SCP服务端也是 SCU客户端。测试工具的本质是让dgate在 SCP 模式下接收数据同时用配套工具主动发起 SCU 请求形成双向验证闭环。关键不在于堆参数而在于剥离 PACS/设备依赖先让本机两个进程完成一次完整 C-STORE。2.1 下载与基础目录初始化避开安装包陷阱Conquest 官网conquestmedical.com提供的 Windows 二进制包常含旧版 OpenSSL 或缺失dumppix.exe。血泪经验直接下载官方 GitHub Releasehttps://github.com/marco-pivetta/conquest/releases中的conquest-1.5.4a-win64.zip。解压后立即执行# 创建严格隔离的测试目录避免污染全局配置 mkdir C:\conquest_test cd C:\conquest_test copy C:\path\to\conquest-1.5.4a-win64\* . # 必须删除默认的 dicom.db 和 dicom.ini它们绑定 localhost:104 且含 demo 数据 del dicom.db dicom.ini提示Conquest 启动时若发现dicom.ini不存在会自动生成一个极简模板。但该模板禁用日志轮转、关闭 DICOM TLS、未设置 AE Title 映射——这正是测试环境需要重写的起点。2.2 手写 dicom.ini5 行配置决定测试成败dicom.ini是 Conquest 的唯一配置文件其语法类似 INI但section 名称大小写敏感键名不可空格值末尾不可带分号。以下是专为测试设计的最小可行配置保存为C:\conquest_test\dicom.ini[CONQUESTSRV1] # 1. 强制指定 AE Title必须与发送端的 Called AE Title 完全一致 MyAETitle CONQUEST_TEST # 2. 绑定到本机任意可用端口避开 104 被占用风险 TCPPort 11112 # 3. 关键启用详细日志默认只记录 ERROR LogLevel 3 # 4. 指定日志文件路径绝对路径避免相对路径导致日志丢失 LogFile C:\conquest_test\dicom.log # 5. 存储路径必须存在且有写入权限 ImportPath C:\conquest_test\incoming\参数说明LogLevel3是调试级0ERROR, 1WARN, 2INFO, 3DEBUG它会记录 Association 请求细节、C-ECHO 响应码、每个 C-STORE 的 SOP Instance UID 和 Transfer Syntax。ImportPath必须提前创建mkdir C:\conquest_test\incoming否则dgate启动失败但无提示。2.3 启动服务并验证端口监听以管理员身份运行 CMDWindows或 TerminalmacOS/Linux进入C:\conquest_test目录# 启动服务-d 参数后台运行-c 指定配置文件 dgate.exe -d -c dicom.ini # 验证端口监听Windows netstat -ano | findstr :11112 # 应返回类似TCP 0.0.0.0:11112 0.0.0.0:0 LISTENING 12345 # 验证进程存在 tasklist | findstr dgate逻辑说明dgate.exe -d启动后会在后台持续运行-c dicom.ini确保加载我们手写的配置。若netstat无输出检查dicom.ini中TCPPort是否被其他程序占用如 VMware、Docker若tasklist无dgate查看dicom.log开头是否有Cannot open database错误——大概率是ImportPath目录不存在或权限不足。2.4 用 dcmdump 发起 C-ECHO 测试确认基础连通性dcmdump.exe是 Conquest 自带的 DICOM 工具集之一用于解析 DICOM 文件或发起简单服务类请求。C-ECHO 是 DICOM 网络连通性的“ping”不传输图像只验证 AE Title 匹配和端口可达# 语法dcmdump [选项] [Called AE Title][IP]:[Port] dcmdump ae CONQUEST_TEST127.0.0.1:11112 sp 1.2.840.10008.1.1 # 成功响应示例最后一行 # I: DUL: Echo response received (status0x0000)参数说明ae指定 Called AE Title必须与dicom.ini中MyAETitle一致sp指定 SOP Class UID1.2.840.10008.1.1是 Verification SOP Class127.0.0.1:11112是目标地址。若返回Association rejected检查dicom.log中是否出现Called AE title does not match—— 此时需核对dcmdump的ae参数与dicom.ini的MyAETitle是否完全一致包括大小写、空格。3. 捕获真实设备流量用 tcpdump dgate 日志交叉定位协议断点临床设备GE、Siemens、国产DR发来的 DICOM 流量常含非标字段、私有 Tag、异常 Transfer Syntax。仅靠dgate日志无法还原原始字节流。真正的测试工具链是让 Conquest 成为“透明代理”把网络包、DICOM 协议帧、存储结果三者对齐。3.1 用 Wireshark/tcpdump 抓取原始 DICOM 流量DICOM 默认使用 TCP 端口 104但 Conquest 测试环境已改为 11112。抓包必须指定该端口# Windows需安装 Npcap # 启动 Wireshark过滤器输入tcp.port 11112 # 或命令行抓包管理员权限 tshark -i Ethernet -f tcp port 11112 -w conquest_traffic.pcap # Linux/macOS sudo tcpdump -i any -w conquest_traffic.pcap port 11112逻辑说明tshark/tcpdump抓取的是原始 TCP 流DICOM 协议封装在 TCP payload 中。Wireshark 可自动解析 DICOM PDUProtocol Data Unit显示 Association Request/Response、C-ECHO/C-STORE 的完整交互帧。关键看Association Request中的Called AE Title、Calling AE Title、Presentation Contexts含 SOP Class 和 Transfer Syntax 列表是否与dicom.ini配置兼容。3.2 解析 pcap 文件定位协议层拒绝原因Wireshark 打开conquest_traffic.pcap后右键任意 DICOM 流 →Follow → TCP Stream。重点观察Association Reject 帧若设备连接后立即断开TCP Stream 中会出现Reject字样。展开DICOM解析树查看Result00Accepted, 01Rejected、Source01Service User, 02Service Provider、Reason01No Reason, 02AE Title Not Recognized, 03Called AE Title Not Recognized...。Transfer Syntax 不匹配在Association Request的Presentation Context中设备列出它支持的 Transfer Syntax如1.2.840.10008.1.2.1 Explicit VR Little Endian。若dicom.ini未声明支持该 SyntaxConquest 会返回Abstract Syntax Not SupportedReason03。提示Conquest 默认支持1.2.840.10008.1.2Implicit VR Little Endian和1.2.840.10008.1.2.1Explicit VR Little Endian。若设备使用1.2.840.10008.1.2.2Explicit VR Big Endian需在dicom.ini中添加[CONQUESTSRV1] # 支持 Big Endian罕见但存在 TransferSyntax 1.2.840.10008.1.2.23.3 dgate 日志与 pcap 交叉验证三步锁定问题当设备发送失败时按时间戳对齐三处信息时间点来源关键线索T0.0sdicom.logReceived association request from XXXXXX 是 Calling AE TitleT0.1sconquest_traffic.pcapWireshark 中Association Request的Calling AE Title字段T0.2sdicom.logAssociation rejected: reason03或C-STORE failed: status0xC001典型问题链dicom.log记录Calling AE Title GE_CT_123pcap显示设备发送的Calling AE Title确实是GE_CT_123dicom.log却报Association rejected: reason02Calling AE Title Not Recognized→ 原因dicom.ini中未配置GE_CT_123的映射。需添加[CONQUESTSRV1] # 允许 GE_CT_123 作为 Calling AE Title 连接 AllowCallingAET GE_CT_123注意AllowCallingAET是白名单机制不配置则默认拒绝所有非本地 Calling AE Title。这是 Conquest 的安全默认但测试时极易踩坑。4. 批量回放与压力测试用 dcm2dcm batch 模拟多设备并发真实场景中CT、MR、DR 设备可能在 1 秒内并发发送 50 个 C-STORE 请求。Conquest 默认配置在高并发下会丢包或超时。测试工具的价值在于复现并量化这种压力下的行为边界。4.1 准备测试 DICOM 文件集从真实设备导出不要用dcm2dcm生成假数据——假数据缺少设备私有 Tag、Study Date 格式异常、Pixel Data 压缩方式不一致。正确做法从 PACS 导出 3~5 个真实病例含 CT/MR/DR确保覆盖不同 Transfer Syntax 和 SOP ClassCT1.2.840.10008.5.1.4.1.1.2CT Image StorageMR1.2.840.10008.5.1.4.1.1.4MR Image StorageDR1.2.840.10008.5.1.4.1.1.1CR Image Storage将文件存入C:\conquest_test\test_data\命名规则CT_001.dcm,MR_001.dcm,DR_001.dcm。4.2 用 dcm2dcm 修改 AE Title 和 Study Instance UIDConquest 要求每个 C-STORE 的Called AE Title必须匹配MyAETitle但真实设备的Called AE Title是固定的如PACS_SERVER。dcm2dcm可批量修改 DICOM 文件头# 修改单个文件的 Called AE Title模拟设备发往 CONQUEST_TEST dcm2dcm sd ca CONQUEST_TEST CT_001.dcm CT_001_modified.dcm # 批量修改整个目录Windows PowerShell Get-ChildItem C:\conquest_test\test_data\*.dcm | ForEach-Object { $out $_.FullName -replace \.dcm$, _mod.dcm dcm2dcm sd ca CONQUEST_TEST $_.FullName $out }参数说明sd保留原始像素数据不重采样ca修改 Called AE TitleCONQUEST_TEST必须与dicom.ini中MyAETitle一致。若跳过此步dgate会直接拒绝C-STORE请求Status0xA900Refused: Not Our SOP Class。4.3 编写批处理脚本模拟 10 设备并发发送创建send_batch.batWindowsecho off setlocal enabledelayedexpansion REM 并发数模拟10台设备 set CONCURRENCY10 REM 每台设备发送的文件数 set FILES_PER_DEVICE3 for /L %%i in (1,1,%CONCURRENCY%) do ( start cmd /c for /L %%j in (1,1,%FILES_PER_DEVICE%) do (dcmsend ae CONQUEST_TEST127.0.0.1:11112 sp 1.2.840.10008.5.1.4.1.1.2 C:\conquest_test\test_data\CT_%%j_mod.dcm timeout /t 1 nul) ) echo Started %CONCURRENCY% concurrent senders. pause逻辑说明start cmd /c启动独立 CMD 进程实现并发dcmsend是 Conquest 的 SCU 工具sp指定 SOP Class UIDtimeout /t 1避免瞬间洪峰压垮dgate。运行后观察dicom.log中C-STORE received的时间戳分布——若大量请求集中在同一秒且后续日志停滞说明dgate线程池已满。4.4 调整 dgate 并发参数解决线程饥饿Conquest 默认最大并发 Association 数为 5。当send_batch.bat启动 10 个进程时后 5 个会排队等待。在dicom.ini中增加[CONQUESTSRV1] # 最大并发 Association 数默认5测试建议设为20 MaxAssociations 20 # 每个 Association 的最大 C-STORE 并发数默认1可设为3 MaxCStorePerAssoc 3 # 关键增加工作线程数默认1瓶颈所在 WorkerThreads 4参数说明WorkerThreads是dgate处理 C-STORE 的线程数。MaxAssociations控制同时建立的 DICOM 连接数MaxCStorePerAssoc允许单个连接内并发处理多个 C-STORE需设备支持。调高WorkerThreads后dicom.log中C-STORE received与C-STORE completed的时间差显著缩短。5. 避坑指南Conquest DICOM Server 测试中最常翻车的 4 个硬伤Conquest 的简洁性是一把双刃剑——配置项少意味着错误反馈弱一个字符的失误就能导致整个测试链路静默失败。以下是我在 12 家医院 PACS 实施中记录的高频问题按现象、原因、解决三步拆解5.1 现象dgate.exe启动后立即退出dicom.log为空原因dicom.ini中ImportPath目录不存在或当前用户对该目录无写入权限Windows UAC 限制。Conquest 在初始化数据库时失败但不写日志直接退出。解决确认ImportPath目录已手动创建mkdir C:\conquest_test\incoming右键目录 →属性 → 安全 → 编辑 → 添加当前用户 → 勾选“完全控制”用icacls C:\conquest_test\incoming /grant Users:F命令授予权限。5.2 现象dcmdump ae ...返回Association rejected: reason03但dicom.log无详情原因reason03是Abstract Syntax Not Supported即设备请求的 SOP Class如1.2.840.10008.5.1.4.1.1.2未被 Conquest 启用。默认 Conquest 只启用基础 SOP ClassCT/MR/DR 需显式声明。解决在dicom.ini中添加[CONQUESTSRV1] # 启用 CT、MR、DR 存储 SOP Class SOPClass 1.2.840.10008.5.1.4.1.1.2 SOPClass 1.2.840.10008.5.1.4.1.1.4 SOPClass 1.2.840.10008.5.1.4.1.1.15.3 现象设备发送成功dicom.log显示C-STORE completed但incoming\目录无文件原因Conquest 默认将接收的 DICOM 文件重命名并存入incoming\的子目录按 StudyInstanceUID而非直接存入根目录。incoming\下看到的是空目录实际文件在incoming\1.2.345.678.901.234\类似路径中。解决查看dicom.log中C-STORE completed行找到StudyInstanceUID字段进入C:\conquest_test\incoming\用dir /s /b *.dcm全局搜索若需扁平化存储修改dicom.ini[CONQUESTSRV1] # 禁用子目录所有文件存入 incoming\ 根目录 StoreInSubDir 05.4 现象dcmsend发送大文件100MB时超时dicom.log记录Timeout waiting for response原因DICOM 默认 TCP 读取超时为 30 秒大文件传输尤其压缩 JPEG2000可能超过此限。dgate在等待 C-STORE-RSP 时断开连接。解决在dicom.ini中延长超时[CONQUESTSRV1] # 增加 Association 和 C-STORE 超时单位秒 AssociationTimeout 120 CStoreTimeout 300注意CStoreTimeout必须 ≥AssociationTimeout否则无效。修改后需重启dgate.exe。6. 进阶技巧用 Python 脚本自动化日志分析与失败归因当测试规模扩大如 100 设备、1000 图像人工翻dicom.log效率极低。我写了一个轻量 Python 脚本log_analyzer.py它不依赖第三方库仅用标准库解析日志5 分钟内输出结构化报告。6.1 脚本核心逻辑提取关键事件链Conquest 日志格式固定[YYYY-MM-DD HH:MM:SS] Level: Message。脚本按时间顺序扫描识别三类事件事件类型触发关键词提取字段Association RequestReceived association requestCalling AE Title, Called AE TitleC-STORE ReceivedC-STORE receivedSOP Instance UID, Transfer SyntaxC-STORE CompletedC-STORE completedStatus Code, StudyInstanceUID# log_analyzer.py import re from collections import defaultdict def parse_dicom_log(log_path): events [] with open(log_path, r, encodingutf-8) as f: for line in f: # 提取时间戳和日志级别 match_time re.match(r\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w):, line) if not match_time: continue timestamp, level match_time.groups() # Association Request if Received association request in line: ae_match re.search(rfrom ([^\s]), line) if ae_match: events.append({ type: ASSOC_REQ, time: timestamp, calling_ae: ae_match.group(1), called_ae: re.search(rCalled AE title: ([^\s]), line).group(1) if Called AE title in line else }) # C-STORE Received elif C-STORE received in line: uid_match re.search(rSOP Instance UID: ([^\s]), line) ts_match re.search(rTransfer Syntax: ([^\s]), line) if uid_match and ts_match: events.append({ type: CSTORE_RECV, time: timestamp, sop_uid: uid_match.group(1), transfer_syntax: ts_match.group(1) }) # C-STORE Completed elif C-STORE completed in line: status_match re.search(rstatus(0x[0-9A-F]{4}), line) if status_match: events.append({ type: CSTORE_COMP, time: timestamp, status: status_match.group(1) }) return events # 使用示例 if __name__ __main__: events parse_dicom_log(rC:\conquest_test\dicom.log) # 统计失败率 total_stores sum(1 for e in events if e[type] CSTORE_RECV) failed_stores sum(1 for e in events if e[type] CSTORE_COMP and e[status] ! 0x0000) print(f总接收 C-STORE: {total_stores}) print(f失败 C-STORE: {failed_stores} (失败率: {failed_stores/total_stores*100:.1f}%)) # 找出最常失败的 Transfer Syntax ts_failures defaultdict(int) for e in events: if e[type] CSTORE_RECV: ts e.get(transfer_syntax, UNKNOWN) # 查找后续 C-STORE COMP 是否失败 next_comp next((x for x in events if x[type]CSTORE_COMP and abs((datetime.strptime(x[time], %Y-%m-%d %H:%M:%S) - datetime.strptime(e[time], %Y-%m-%d %H:%M:%S)).total_seconds()) 10), None) if next_comp and next_comp[status] ! 0x0000: ts_failures[ts] 1 print(\n按 Transfer Syntax 统计失败数:) for ts, cnt in sorted(ts_failures.items(), keylambda x: x[1], reverseTrue): print(f {ts}: {cnt} 次)逻辑说明脚本不依赖pydicom或pynetdicom仅用正则提取日志关键字段。C-STORE失败归因的核心是时间邻近性——C-STORE received与C-STORE completed的时间差 10 秒视为同一事务。通过统计各 Transfer Syntax 的失败频次可快速定位是1.2.840.10008.1.2.4.70JPEG Lossless还是1.2.840.10008.1.2.4.90JPEG 2000导致问题进而决定是否在dicom.ini中禁用该 Syntax。6.2 结合 Wireshark 导出 CSV 进行根因分析Wireshark 可将conquest_traffic.pcap导出为CSVFile → Export Packet Dissections → As CSV包含Time,Source,Destination,Length,Info。用 Excel 或 Pandas 加载后与log_analyzer.py输出的SOP Instance UID关联CSV Info 字段对应 DICOM 元素用途Association RequestCalled AE Title验证设备是否发送正确 AE TitleC-STORE RQSOP Instance UID与dicom.log中C-STORE received的 UID 对齐C-STORE RSPStatus直接获取设备返回的状态码如0xC001 Failure当log_analyzer.py报告某 UID 失败但在dicom.log中找不到对应C-STORE completed行时查 CSV 中该 UID 的C-STORE RSP—— 若 CSV 有响应而日志无记录说明dgate进程崩溃或磁盘满若 CSV 也无C-STORE RSP则是设备端未发送响应需检查设备网络或防火墙。我坚持用 Conquest 测试工具的底层逻辑它不承诺完美兼容但承诺把每一字节的协议交互、每一次内存分配、每一个线程阻塞都暴露给你。与其花三天调试一个黑盒 PACS 接口不如用两小时搭好 Conquest 测试环境让dicom.log和pcap成为你诊断 DICOM 问题的 X 光片。那些被忽略的reason03、静默丢弃的1.2.840.10008.1.2.4.90、以及WorkerThreads1导致的队列堆积从来不是 Conquest 的缺陷而是临床影像数据流转中真实存在的毛细血管级堵点。希望帮到你。本文还有配套的精品资源点击获取
返回列表