背景:

之前专栏中介绍最多的两款PACS各自是基于dcmtk的dcmqrscp以及Orthanc。和基于fo-dicom的DicomService(自己开发的)。该类应用场景都是针对于局域网,因此在使用DIMSE-C各项服务时并未遇到的复杂问题,学习和使用成本相对较低。

通过近一年的时间也已经对C-ECHO、C-FIND、C-STORE、C-MOVE、N-PRINT等各项服务都进行了详细介绍,而且从DICOM标准来看C-MOVE服务能够包括C-GET,因此今天之前专栏中从未介绍过C-GET服务。

最近因为项目须要,開始频繁接触基于dcm4che的dcm4chee服务。通过托管于JBoss AS应用server中dcm4chee能够为互联网用户提供DICOM服务(注意差别于之前介绍的Orthanc的RESTful服务,以及DICOM标准中的WADO协议)。

通过直接将DICOM服务部署到外网主机中来实现web的PACS服务,因为DICOM协议本身是建立在TCP上,所以从理论上这样的DICOM服务的web化是可行的。

起初在对接C-FIND、C-STORE等服务时也非常顺利,与常规的局域网操作全然同样。无非就是訪问的对端AE节点IP地址是公网IP而已。可是在进行C-MOVE操作时,却总是无法成功。

dcm4chee的AE Title配置服务:

起初以为dcm4chee对C-MOVE的实现有问题。于是自己在本地Ubuntu虚拟机中搭建部署了一套dcm4chee服务。

通过单步调试,以及检索dcm4che官网,找到了部分线索:dcm4che的AE Title Configuration Service

如官网中所述。dcm4chee因为部署到互联网中,相较于局域网须要配置SCU和SCP各自AETitle信息的情形,dcm4chee额外加入了一种自己主动配置模式,即假设请求端(即不论什么SCU)开放的port是104或11112。dcm4chee会自己主动将该SCUclient的AETitle加入到配置中,同意其与dcm4chee建立连接。依照官方说明。改动请求端的port为11112,再次尝试。

C-FIND依旧能够返回结果。而C-MOVE却始终无法下载数据到本地。通过查看dcm4chee后台日志发现,总是提示无法建立连接。那么究竟是不是dcm4chee服务出的问题呢?。通过使用dcm4che源代码中自带的工具包dcmqr.bat发现能够顺利从部署到外网的dcm4cheeserver中下载数据到本地,详细操作指令例如以下:

dcmqr DCM4CHEE@XXX.XXX.XXX.XXX:11112 -cget -nocfind -q0020000D=1.3.6.1.4.1.30071.6.10987654321.1234567890 -cstore CT -cstoredest c:\test

输出日志截图例如以下:



在本地c:\test文件夹下能够看到下载成功的dcm文件。

C-GET与C-MOVE:

既然dcmqr.bat工具能够顺利将数据下载到本地,能够确信dcm4chee服务端实现没有问题。

那么究竟是哪里出现了问题呢?让我们接着往下看:

同样使用dcmqr.bat工具,输入下述指令:

dcmqr DCM4CHEE@XXX.XXX.XXX.XXX:11112 -cmove ZSSURE -nocfind -q0020000D=1.3.6.1.4.1.30071.6.198690283870599.4896581024566519 -cstore CT -cstoredest c:\test

却总是提示无法识别Move Destination AETitle:ZSSURE,例如以下截图所看到的各自是dcm4chee服务端和dcmqr.batclient的额输出日志:



至此应该能够大致确定问题出如今C-MOVE服务的实现机制上,通过dcmqr.bat工具的測试。能够确定C-GET服务能够顺利从部署到互联网的dcm4cheeserver下载数据,而C-MOVE服务无法实现。



以下就详细分析一下C-GET与C-MOVE的差别:



从DICOM3.0标准官方描写叙述中能够看出,两者的目的是同样的。且主动发起请求方(即SCU)的起始操作是同样的——都是从SCP端查询匹配项。

可是随后的子操作不同。在C-GET SERVICE中的描写叙述是子操作的触发在同一个连接中(on the same Association);而在C-MOVE SERVICE中的描写叙述是子操作的触发在单独的、另外的一个连接中(on a separate Association)

为了更清楚的描写叙述两者的差别,请看以下两幅示意图:



图中用双向箭头来示意详细C-GET和C-MOVE服务中所建立的TCP连接数;单项箭头分别表示详细服务中的RQ和RSP,依照官方说法C-GET和C-MOVE服务都是确认服务(Confirmed Service),所以单项箭头都是一一配对的,正常情况下一个RQ相应一个RSP;单项箭头标记的数字表明C-GET和C-MOVE服务详细请求过程中的各消息发送时序。

fo-dicom(mDCM)实现C-GET:

之前分析过fo-dicom(mDCM)库中实现DICOM协议的各种关系。简单来说类就是对某种操作以及操作所需的数据的一种封装,从上面的结构图分析以及官方DICOM3.0标准来看,DICOM协议中的操作主要就是建立&关闭TCP连接、发送&接收请求、读取&解析套接字信息(DIMSE)。以及相关异常处理等等,所以总体的操作都被放到了DcmNetworkBase类中。然后依据不同的角色(即SCU和SCP)。分别派生出DcmServiceBase和DcmClientBase,直至最底层的DcmServer和DcmClient。

简单的类图例如以下所看到的:

依据背景中的描写叙述,我们这里扮演的是C-GET SCU角色。即须要派生DcmClient。细致查看fo-dicom(mDCM)源代码发现当中并未实现C-GET相关的不论什么client(SCU)和服务端(SCP)类的封装。为了借鉴现有经验又查看了一下dcmtk和dcm4che的源代码,仅仅有dcm4che提供的dcmqr.bat工具包中嵌入了c-get服务。

通过查看DICOM标准第7部分可知,C-GET服务于C-MOVE服务除了在Association上不同以外。其它參数以及操作流程差点儿全然同样。

既然fo-dicom(mDCM)中给出了CMoveClient类,那么我么直接借用CMoveClient类来编写CGetClient类。

起初直接拷贝了CMoveClient类源代码。去掉了当中关于第三方DestinationAE的相关成员变量,在測试过程中总是出现Association.Available==0的情况,例如以下图所看到的:



后来通过对照DICOM标准以及上述分析发现。在CMoveClient类中并未实现DcmNetworkBase类的OnReceiveCStoreRequest方法,而C-GET服务依照我们之前的分析须要在同一个Association中处理来C-GET SCP的C-STORE SCU,因此对CGetClient类的OnReceiveCStoreRequest进行扩展。加入保存DcmDataset到.dcm文件的代码(注:这里为了方便用户后期扩展,将详细的实现放到了代理delegate中。由用户在Dicom.dll库外部自己定义实现)
大致的代码例如以下所看到的:

        protected override void OnReceiveCStoreRequest(byte presentationID, ushort messageID, DicomUID affectedInstance,
DcmPriority priority, string moveAE, ushort moveMessageID, DcmDataset dataset, string fileName)
{
try
{
if (OnCStoreRequest != null)
OnCStoreRequest(presentationID, messageID, affectedInstance, priority, moveAE, moveMessageID, dataset, fileName);
SendCStoreResponse(presentationID, messageID, affectedInstance, DcmStatus.Success); }
catch (System.Exception ex)
{
SendCStoreResponse(presentationID, messageID, affectedInstance, DcmStatus.ProcessingFailure); }
Console.WriteLine("c-move c-store RQ!");
}

加入OnReceiveCStoreRequest实现代码后。再一次连接dcm4cheeserver后能够看到顺利接收到了DcmDataset并保存到本地.dcm文件。

至此CGetClient类的封装工作就完毕了。

知识点补充:

TCP全双工:

全双工协议(即Full-Duplex Transmissions),是指通信时同意两个方向同一时候传递数据,他相当于两个单工通信。它要求发送设备和接收设备都有独立的接收和发送能力。

通熟点来讲在通信时能保证在随意时刻两方都能听到对方的声音。(摘自《全双工和半双工的差别》)。TCP协议就是全双工协议,因此在上述实现C-GET请求时能够再同一个TCP连接中(即Association)实现C-STORE和C-GET。

外网VS内网:

IP地址资源紧张,255.255.255.255形式的32位地址已经远不能标识互联网中的全部主机。而外网和内网的划分在一定程度上攻克了IP地址资源紧张的问题。每一个内网通过路由映射到一个外网IP地址,内网中的主机经过路由器之后才干訪问外网,而对外他们仅仅耗费了一个外网IP地址资源。

常见的内网IP有以下几种类型:

10.0.0.0~10.255.255.255

172.16.0.0~172.31.255.255

192.168..0.0~192.168.255.255


所以仅仅要拥有外网IP。就能够直接连接互联网,而不须要经过路由器或交换机。因为外网IP资源宝贵,且属于全球化资源,因此外网IP一般都是用于公司企业、学校、政府等机构。除了上述三类内网IP以外的地址都是外网IP

外网訪问内网:

自媒体的火热,以及Github、Gitlib等的普及,人人都希望建立自己的独立博客、独立站点。

之前博文介绍的就是一种公布个人站点的一种方式。这样的方式费用较高,须要购买server(拥有外网IP的主机),能够简单的觉得就是购买外网IP地址,以及域名。大家能够訪问www.zssure.me体验一下我搭建的个人主页。

那么除此以外有没有其它方法能够公布个人主页呢?搜索一下就能够看到非常多博文介绍怎样使用路由的虚拟server(即port映射)来实现外网訪问内网的功能呢,如是也能够简单的公布个人主页。当然这样的方式的危急系数高,易受到黑客攻击,不建议大家使用。

关于从外网直接訪问内网的设置可參见路由器实现外网訪问内网

大致的流程是利用路由器的虚拟server功能,进行port映射,实现外网IP地址到内网IP+port的一一映射。从而实现外网直接訪问内网的目的。示意图例如以下(图片来源于路由器实现外网訪问内网):

内网訪问外网:

内网訪问外网的情况就比較常见。诸如公司、学校等机构局域网内能够上网的都是内网訪问外网。通常通过NAT(Network Address Translation)来实现。百度百科对NAT的描写叙述例如以下:

NAT(Network Address Translation,网络地址转换)是1994年提出的。当在专用网内部的一些主机本来已经分配到了本地IP地址(即仅在本专用网内使用的专用地址),但如今又想和因特网上的主机通信(并不须要加密)时。可使用NAT方法。

这样的方法须要在专用网连接到因特网的路由器上安装NAT软件。

装有NAT软件的路由器叫做NAT路由器,它至少有一个有效的外部全球IP地址。这样,全部使用本地地址的主机在和外界通信时,都要在NAT路由器上将其本地地址转换成全球IP地址。才干和因特网连接。

另外。这样的通过使用少量的公有IP 地址代表较多的私有IP 地址的方式,将有助于减缓可用的IP地址空间的枯竭。

在RFC-1632中有对NAT的说明。

利用NAT有两大优点。各自是宽带分享和安全防护。

对内能够多台主机共享一条宽带。对外多带主机相应一个公网IP,因此当受到外网攻击时无法确定相应的单一主机。(这也就是C-MOVE请求訪问基于WEB的dcm4cheeserver终于失败的原因。

虚拟server:

VPN:

对于虚拟server和VPN的介绍临时搁置一下,因为后期须要手动搭建VPN服务因此放到下一篇博文中再介绍。敬请期待!

PS:能够对照之前专栏中的博文DICOM医学图像处理:AETitle在C-FIND和C-MOVE请求中的设置问题来分析一下内网与外网的差别。这里特此说明当时的一种解决方式(即利用TcpClient来直接反推IP地址)的做法是有局限性的,在局域网中是可行的,在互联网大环境下会失败。

修正:博文中C-GET服务示意图中C-GET-RSP、C-STORE-RQ、C-STORE-RSP等消息的流程是错误的,详情能够參见兴许的博文DICOM:C-GET服务

作者:zssure@163.com

时间:2015-07-13

DICOM:C-GET与C-MOVE对照剖析的更多相关文章

  1. DICOM:C-GET服务

    背景: 之前博文对照过多次C-MOVE与C-GET服务的差别,两者最大的差别在于C-GET是基于单个TCP连接的点对点的双方服务.而C-MOVE是基于两个TCP连接的三方服务(详情參见:<DIC ...

  2. DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)

    转载:http://blog.csdn.net/zssureqh/article/details/39237649 背景: 上一篇博文中,在对storescp工具源文件storescp.cc和DcmS ...

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

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

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

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

  5. DICOM:再次剖析fo-dicom中DicomService的自己定义事件绑定

    题记: 趁着<从0到1>大火的热潮,最近又一次翻阅了一遍<从一到无穷大>(这样是不是感觉整个非负数轴就圆满了^_^). 尽管作为科普类书籍.可是里面的内容还是比較深奥,幸亏有作 ...

  6. DICOM医学图像处理:开源库mDCM与DCMTK的比較分析(一),JPEG无损压缩DCM图像

    背景介绍: 近期项目需求,须要使用C#进行最新的UI和相关DICOM3.0医学图像模块的开发.在C++语言下,我使用的是应用最广泛的DCMTK开源库,在本专栏的起初阶段的大多数博文都是对DCMTK开源 ...

  7. 【Redis源代码剖析】 - Redis内置数据结构之压缩字典zipmap

    原创作品,转载请标明:http://blog.csdn.net/Xiejingfa/article/details/51111230 今天为大家带来Redis中zipmap数据结构的分析,该结构定义在 ...

  8. dicom网络通讯入门(2)

    第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了. 首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么.同学我告诉你还早着呢. ...

  9. eclipse菜单解释及中英对照

    在使用Eclipse作为开发工具的时候,建议使用英文版本的(直接百度从官网下就行,这里不详细描述,如果有问题,咱们私聊).虽然中文版本的对于和我一样对英文是小白的看起来特别爽,但是公司大多是英文版本的 ...

随机推荐

  1. PLSql连接远程Oracle方法

  2. [Ember] Wraming up

    1.1: <!DOCTYPE html> <html> <head> <base href='http://courseware.codeschool.com ...

  3. [J2EE学习][post,get乱码处理]

    post乱码 通过flitter过滤(原理待未来好好学习) <!-- post乱码过虑器 --> <filter> <filter-name>CharacterEn ...

  4. [HeadFirst-HTMLCSS入门][第九章盒模式]

    新属性 line-height 行间距 line-height: 1.6em; border 边框 属性值 solid 实线 double 双实线 groove 一个槽 outset 外凸 inset ...

  5. jBPM4.4与SSH2整合

    整合jBPM的目的就是能够通过注入的方式得到ProcessEngine实例,因为ProcessEngine是jbpm 的核心. 整合步骤: 1.新建web程,搭建好SSH2环境 2.导入jbpm相关的 ...

  6. javascript延迟加载及异步(defer和async)

    一直以来写代码的时候的常用习惯就是吧所有的js文件直接加载在文档的head标签里面,在写js文件的时候有时候获取一些文件对象的时候为空对象,这是由于文档结构还没有加载完,但是js文件已经加载完.也就是 ...

  7. POI操作Excel常用方法总结

    转载自:http://blog.csdn.net/xjun15/article/details/5805429 一. POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提 ...

  8. 序列化layer创建的弹出表单并ajax提交

    /** *createTime:2015-09-13 *updateTime:2015-09-13 *author:刘俊 *phone:13469119119 *QQ:418873053 **/ va ...

  9. 关于《Cocos2d-x建工程时避免copy文件夹和库》的更新

    在前几篇博文中大概了解了Cocos2d-x引擎的基本结构后打算开始实际操作,便在网上转载了一篇关于VS新建Cocos2d-x项目的文章.今天实际操作的时候发现博主使用的引擎版本和我的不一致(<C ...

  10. PROCEDURE_监测系统_数据备份存储过程—备份原始数据,每十分钟一条,取平均值

    create or replace procedure proc_backup_originaldata(retCode out varchar2, -- 返回码                    ...