转载:http://blog.csdn.net/zssureqh/article/details/39237649

背景:

上一篇博文中,在对storescp工具源文件storescp.cc和DcmSCP类的源文件scp.cc进行剖析后,得出了两者都可以实现响应C-ECHO和C-STORE(需要对DcmSCP类进行扩展)请求的功能。但是在对DcmSCP类进行扩展,期望模拟实现自己的storescp.exe工具时遇到了问题,客户端提示服务中断链接,而服务端显示保存失败,如下图所示。此次博文通过排除该问题再一次对storescp.cc和scp.cc进行对比,主要从Presentation Context、AbstractSyntax、TransferSyntax等细节出发,认真学习DICOM通讯服务。

问题排除:

1)对比分析storescp.exe工具包与自定义工具包的调试信息

为了排除storescu.exe客户端的问题,大致确定问题出现的范围,决定再一次用storescp.exe作为客户端,使用storescu.exe对其发送C-STORE请求,查看storescu.exe客户端的调试信息。与我们的自定义工具服务端的调试信息进行对比。

对比上图中的调试信息,其中红色部分END A-ASSOCIATE-AC表示的是服务端已经顺利的与客户端完成了握手,即网络已经顺利响应了客户端的连接。蓝色部分表示客户端发送的C-STORE-RQ请求也已经顺利的到达了服务端,唯一不同的就是黄色圆圈标记的部分,说明在服务端接收到C-STORE-RQ请求后对其处理有问题。而我们自己封装的ZSDcmStoreSCP类对于C-STORE-RQ的处理函数是直接拷贝的storescp.exe工具内的storeSCP和storeSCPCallback函数。因此大致可以确定问题可能出现的范围。

2)单步调试

在handleIncomingCommand函数内部调用storeSCP指令处插入断点,进入单步调试状态。利用VS2012提供的新的调试工具“并行堆栈”,找到了第一个返回状态cond.bad()为true的地方,即函数ASC_findAcceptedPresentationContext,如下图所示:

继续单步调试,进入到ASC_findAcceptedPresentationContext函数内部,发现真正出现错误的地方是findPresentationContextID,该函数的参数presentationContextID始终为零。

猜测:可能是presentationContextID参数在传递过程中的某一环节出错了,导致程序失败。为了验证我们的想法,打开storescp.exe工程,进入调试模式,查看参数的数值情况,如下图所示,storescp.exe工具包中presentationContextID参数的值为41。因此证明了我们在presentationContextID参数传递的过程中发生了错误。

3)参数传递流向

为了确定具体传递过程中的错误环节,我们采取回溯的方式,通过回溯findPresentationContextID函数中presentationContextID参数的来源来确定问题出现的具体位置。

(注:为了方便讲图片旋转了90度,劳烦大家歪着脖子将就着看一下下吧哈)

从上图中可以看出T_ASC_PresentationContextID参数最终的来源是我们重载DcmSCP基类的handleIncomingCommand函数,因此与storescp.exe中的是实现对比,仔细查看该部分的代码,发现了一个重大问题,原来我们在将storescp.exe工具中的processCommands函数内容拆解到handleIncomingCommand函数内部时,将多余的DIMSE_receiveCommand函数注释掉的同时,遗漏了注释掉T_ASC_Association局部变量,使得原本应该通过DIMSE_receiveCommand函数获取的presID变量一直被局部变量覆盖为0——至此问题找到了。

那么由于注释掉了多余的DIMSE_receiveCommand函数,我们从何处来获取presID参数呢。查看一下handleIncomingCommand函数的头发现函数参数中也没有出现T_ASC_PresentationContextID类型的参数。但是对比scp.cc原本处理C-ECHO请求的函数handleECHORequest我们发现,scp.cc类中将presID的值存储在了DcmPresentationContextInfo类型的presInfo变量中。因此我们修改storeSCP的调用,将presInfo.presentationContextID传递进入作为presID的初值。

修改完成后,再次调试发现程序运行正常,客户端顺利收到了服务端反馈回来的DIMSE Message,并且在服务端的目录下也看到了storescu.exe传递过来的dcm文件(当然在storeSCP函数内部根据时间等信息对文件进行了重命名)。

至此上一博文中自模拟storescp.exe工具包的问题已经顺利解决,利用我们自己封装的ZSDcmStoreSCP类可以简单地实现storescp.exe工具包的功能。

总结分析:

此次调试排除错误的过程中发现,在组合移接不同文件的代码时要格外的注意细节部分。顺便借着此次传递丢失T_ASC_Association类型参数的问题,我们详细的分析一下传递失败的T_ASC_Associaton类型参数的作用,为什么简单的一个参数传递失败,就会导致C-STORE功能失效?

1)Presentation Context、AbstractSyntax、TransferSyntax细节学习

在上一篇博文中我们摘录了DICOM3.0标准中关于Presentation Context、AbstractSyntax、TransferSyntax几个名词的解释,通俗一点来讲就是说Presentation Context代表的是两个应用实体(AE=Applicaiton Entity)交互的环境(通常叫做上下文),它包含后面的AbstractSyntax和TransferSyntax名词。AbstractSyntax与TransferSyntax比较容易混淆就是因为英文单词中都包含了Syntax,其实两者完全是不同的概念,AbstractSyntax关注的是上层信息,即两个交互的AE之间 进行的是何种交互,也就是标准中所说的服务对象对(SOP)的类型,通过查看dcuid.h文件可知,AbstractSyntax有Store、Query/Retrive、Worklist等类型,例如Store类型的UID_CTImageStorage 、Query/Retrive类型的UID_FINDPatientRootQueryRetrieveInformationModel、Worklist类型的UID_FINDModalityWorklistInformationModel等等。这几个是我们在C-ECHO、C-STORE、C-FIND服务中常用到的几种;而TransferSyntax关注的是信息传输交互时的编码规则,是Explicit还是Implicit,是LittleEndian还是BigEndian,常见的有UID_LittleEndianImplicitTransferSyntax、UID_LittleEndianExplicitTransferSyntax等等,具体的可以自行查看dcuid.h文件。而上面传递丢失的presID参数就是包括了这两种最重要的交互信息。因此会导致整个C-STORE服务失败。

2)storescp.cc与scp.cc整体再对比

那么storescp.exe工具包和DcmScp类又是在什么时刻?什么位置来设置这些参数的呢?下面我们就再一次对比分析一下两种工具的实现流程(细节可参照上一篇博文DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求)。

2.1 scp.cc源码文件

借助上一篇博文的图片来分析一下DcmSCP类中对于Presentation Context(即AbstractSyntax和TransferSyntax)的具体操作。

上图中给出了DcmSCP类中设置Presentation Context上下文环境的具体位置和使用的函数。

2.2 storescp.exe源码文件

想必storescp.exe工具包中的设置流程也基本是相似的,接下来我们同样借助上一篇博文的图来分析一下。

对比两个图片我们可以发现DcmSCP和storescp.exe都是在处理真正的DIMSE消息之前对连接的Presentation Context上下文进行配置,至于这么多UID(无论是AbstractSyntax还是TransferSyntax都是在dcuid.h文件中用统一的UID来定义的)存储到哪里了呢?结合上一篇博文的分析,我们知道外部循环开始后主要的核心函数都有一个T_ASC_Association类型的参数assoc(storescp.exe中)或m_assoc(DcmSCP类中),查看一下T_ASC_Association类型的定义,并逐级打开,可以发现正如我们预料中的一样,该变量中存储了连接的上下文的所有UID,如下图所示:

至此我们对于DcmSCP类和storescp.exe工具的源码的剖析总算告一段落了,希望大家能够对DICOM的网络通讯服务有一个更深刻的认识。

3)对新版DCMTK中DcmSCP和DcmStoreSCP实现的初步猜想

通过此次使用DcmSCP类的工程发现,该类的设计有些欠妥,开放的接口不够合理,无法轻松的实现扩展来响应C-STORE等其他请求。猜想新版的DCMTK肯定会进行修改,通过查看dcmtk3.6.1的官方说明,发现新版的dcmtk开源库中的确对DcmSCP和DcmSCU基类进行了大的修改,开放了众多新的接口(如下图)方便用户进行扩展,另外新版的库中直接给出了封装好的DcmStoreSCP和DcmStoreSCU类,所以大家就不要使用我给出的代码ZSDcmStoreSCP类啦,仅供测试使用,具体运用是还是赶紧安装最新版的dcmtk开源库吧。

实例工程代码下载:

1)CSDN资源下载:

连接:http://download.csdn.net/detail/zssureqh/7906309,需要1个积分奥。

2)Github免费下载

连接:https://github.com/zssure-thu/CSDN

后续专栏博文预告:

1)dcmtk3.6.0的DcmSCP与dcmtk3.6.1的DcmSCP分析,以及dcmtk3.6.1的DcmStoreSCP和DcmStoreSCU的使用

2)Dcmtk与fo-dicom保存文件的不同设计模式:单线程VS多线程

3)wlmscpfs.exe与findscu.exe的源码剖析:学习C-FIND请求

DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)的更多相关文章

  1. DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求

    转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...

  2. DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM

    背景: 从DICOM网络传输一文开始,相继介绍了C-ECHO.C-FIND.C-STORE.C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只 ...

  3. [转]DICOM医学图像处理:Deconstructed PACS之Orthanc

    转载:http://blog.csdn.net/zssureqh/article/details/41424027 背景: 此篇博文介绍一个开源的.基于WEB的DICOM Server软件.该开源软件 ...

  4. DICOM医学图像处理:Deconstructed PACS之Orthanc

    背景: 此篇博文介绍一个开源的.基于WEB的DICOM Server软件.该开源软件完全使用C++编写,不依赖于第三方数据库(内置了SQLite数据库)或其他框架,支持RESTful API设计模式. ...

  5. DICOM医学图像处理:fo-dicom网络传输之 C-Echo and C-Store

    背景: 上一篇博文对DICOM中的网络传输进行了介绍.主要參照DCMTK Wiki中的英文原文.通过对照DCMTK与fo-dicom两个开源库对DICOM标准的详细实现,对理解DICOM标准有一个更直 ...

  6. DICOM医学图像处理:深入剖析Orthanc的SQLite,了解WADO & RESTful API

    背景: 上一篇博文简单翻译了Orthanc官网给出的CodeProject上“利用Orthanc Plugin SDK开发WADO插件”的博文,其中提到了Orthanc从0.8.0版本之后支持快速查询 ...

  7. DICOM医学图像处理:Orthanc Plugin SDK实现WADO服务

    背景: Orthanc是博主发现的一个很完美的DICOM和HTTP服务端开源软件,前几篇分别介绍了Orthanc的基本使用.Orthanc从0.8.0版本之后给出了Plugin SDK,通过该SDK可 ...

  8. DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class

    背景: 预告了好久的几篇专栏博文一直没有整理好,主要原因是早前希望搭建的WML服务器计划遇到了问题.起初以为参照DCMTK的官方文档wwwapp.txt结合前两天搭建的WAMP服务器可以顺利的实现WM ...

  9. DICOM医学图像处理:WEB PACS初谈

    背景: 周末看到了一篇原公司同事的文章,讲的是关于新的互联网形势下的PACS系统.正好上一篇专栏文章也提到了有想搭建一个worklist服务器的冲动,所以就翻箱倒柜将原本学生时代做课题时搭建的简易We ...

随机推荐

  1. NoSQL数据库之国产开源产品:SequoiaDB 分析前言

    随着互联网技术的发展,面对海量数据的存储和分析,传统关系型数据库已经无法满足,由此衍生出一种与关系型数据库区别开的数据库NoSQL(Not Only SQL). 国外做的比较成熟的NoSQL有Mong ...

  2. 一条命令使win7可以直接运行.net3.5程序

    dism /online /get-features 获取 功能名称 dism /online /enable-feature /featurename:NetFx3  启用win7自带的.net f ...

  3. chromiun 学习《一》

    众所周知,Chrome是建立在开源的Chromium项目上的. 而且不得不说,学习并分析开源项目的代码对一个程序员的提高确实蛮大的.这篇博文我会记录一下学习过程中我遇到的一些问题,并分享学习中我所参考 ...

  4. User Attributes - Inside Active Directory

    User Attributes - Inside Active Directory Related to the book Inside Active Directory, ISBN 0-201-61 ...

  5. JS实现Ajax---例:获取服务器时间

    Ajax在本质上是一个浏览器端的技术 XMLHttpRequest XMLHttpRequest对象 XMLHttpRequest对象在IE浏览器和非IE浏览器中创建的方法不同. 简而言之:它可以异步 ...

  6. RIP、OSPF、BGP、动态路由选路协议、自治域AS

    相关学习资料 tcp-ip详解卷1:协议.pdf http://www.rfc-editor.org/rfc/rfc1058.txt http://www.rfc-editor.org/rfc/rfc ...

  7. TCP/IP详解 学习二

    链路层: 在 T C P / I P协议族中,链路层主要有三个目的:(1)为 I P模块发送和接收 I P数据报:( 2)为 A R P模块发送 A R P请求和接收 A R P应答:( 3)为 R ...

  8. The prefix "mvc" for element "mvc:annotation-driven" is not bound 的解决方法

    添加 xmlns:mvc="http://www.springframework.org/schema/mvc" http://www.springframework.org/sc ...

  9. groovy-位运算

    从Groovy 1.0 beta 10开始,Groovy支持位运算:<<. >>, >>>, |, &, ^, and ~. 下表列出了位运算的操作符 ...

  10. C/C++ 中的指针

    指针(pointer)有两种涵义 一是指C/C++中的一种数据类型(data type). 二是指某个类型为指针的 数据/物件(object). 指针作为一种数据类型,属所谓复合类型(compound ...