【转】dicom网络通讯入门(2)

转自:dicom网络通讯入门(2) - assassinx - 博客园

首先我们现一个echo响应测试工具,也就是echo 的scu,不是实现打印作业管理么。同学我告诉你还早着呢。本来标题取的就是《dicomviewer 第二弹 之 实现打印管理》名字多霸气,最后我又改回来了。
 
首先你得把数据组织方式搞懂 那就是pdu  和dimse  元素  数据元素。然后基于这之上你得把协商连接这块搞懂 ,协商连接都没通过不用说后面的了。然后你得把实现一个功能 比如打印 ,scu跟scp之间你来我往的 过程和概念搞懂 也就是dimse 然后才是服务类。最后你全都理解了 并且能跟医院的设备正常 连接和操作了,那么恭喜你 差不多了。

最后要说的是: 解析dicom文件那篇你们都已经看过了,dicom网络通讯跟解析文件是一样的 只不过解析的是socket数据流里的 元素 数据结构本身是一样的,然后他有一些规范和标准 ,这就是dimse 和服务类 这些好像都在dicom标准的第四章  第八章 第七章 。

 
实现这一大坨的东西 有点望而却步了吧,其实总结起来就一句话 概括 按照dicom标准 封装数据 处理数据 ,然后根据特殊的参数和应用场景 依规范响应数据,。
 
好废话少说,开工 看过 标准简介那篇博客 的都知道:
PDU是一种数据结构 dataElement是一种数据结构
pdu结构总共7种 其中用于连接控制的就占了6种
A-Associate-RQ PDU :连接请求协议数据单元,用于关联请求。
A-Associate-AC PDU :连接接受协议数据单元,用于对关联请求的应答。
A-Associate-RJ  PDU :连接拒绝协议数据单元,用于拒绝关联请求。

A-Release-RQ PDU   :连接释放请求协议数据单元,
A-Release-RSP PDU :连接释放响应协议数据单元,
A-Abort PDU
P-DATA-TF PDU        :传输内容的pdu(只有这一种)
当通讯双方建立了关联之后,就可以使用P-DATA-TF所提供的传输服务来实现不同的通信功能了。
 总之你在进行后面的dimse发送之前先得建立连接,否则你什么也搞不了。
好下面就协商连接的pdu进行分析:

你问这图是怎么来的 dicom 第八章 31页。是不是跟上面说的是一样的 开始两字节 然后4字节表示长度,只不过这个更详细了。协商连接pdu说起有6种 其实有好多是大同小异 比如Associate pdu, 他分rq 和ac  rq是请求 ac是响应。
我把协商连接概述一下


概述之前,先解释下什么是通讯:
还是炒剩饭 我又得把以前说过的话像背书一样的背一遍了 其实他确实是那么回事。什么是通讯 :
命令tag +数据tag  一起组成sop ,就好象说一句这样的话:“把这根萝卜拿去给我切了”    “喏  ,萝卜” 。其实这就是通讯  跟人与人之间传达意思一样。说话的时候太熟练了  没察觉到  你要仔细去想  你自己是一台电脑   ,会是一个什么样的步骤。
网络传输 跟文件组织 是一样的格式 。不过有命令tag 。很多命令tag组合到一堆,这称之为dimse。 c-echo n-create c-find c-store 这些都称之为dimse 。而打印管理是由很多组dimse 包括n-create 那些 你来我往的一套组成 比如 先n-create 什么东西 再 n-set什么东西,他有一种逻辑规范 什么参数错误则不进行n-set。这很多组dimse称之为服务类 ,比如打印管理 就是一个服务类。这些规范在dicom标准的第八章有说明。总之在dimse传递之前 你必须得协商连接。
dicom标准的地址是这个Current Edition,英文的 。看也比较困难 装装面子,主要是理解就行了 我看的也是别人翻译的中文的。不过官方的就是官方的 没办法 某些地方你找不到原因 想参照最标准的指示 你还是得硬着头皮去看英文文档。
  
Associate pdu 协商连接的过程:
又说多了 不论如何在进行dimse之前必须得进行连接协商 因为你与别人进行通讯首先你得确定两个东西。 谈话的主题是什么(虚拟语法),你是用哪国语言(传输语法)。这两个东西一个称之为虚拟语法 abstract syntax  一个称之为传输语法 transfersyntax 传输语法其实主要确定两个东西 字节序 和 vr表示方式 ,如果你不知道字节序是什么 请自己百度 vr表示方式 跟文件解析一样的,(字节序 和 vr表示方式?)他们两个一起被称之为表达上下文。注意表达上下文可以有多个 每个都有id。如果你是scp端 那么连接协商响应 也就是association-ac的时候你要告知scu,本scp可以完成哪些表达上下文的服务 传输语法是什么,如果服务不了也要给出对应的上下文id 并进行告知。这样的话scu端知道你服务不了就知难而退 主动断开连接。 其次还有些其他东西比如pdu最大数据长度 一般是0x4000。好了讲完了 这就是协商连接的过程 对照上面的图理解了否。
这是官方的解释:

官方的解释 网络协议是分层的,Dicom ULP称之为dicom上层协议。  也就是上图的dicom ul service provider。 反正要按照osi的标准来, 也就是说要定义一个associate-rq 或者 ac的数据结构来,一切的数据序列化或者反序列化都由 dicom ul service provider 来进行,反正只怕忽悠不死你。反正他说是那样说 我们自己按照自己的方式来。
 
好终于要动代码了 ,我喜欢的事情来了 噢啦啦啦。其实这是一个抽象化的过程,把你的想法付诸行动 代码化.就像某人说过的 主要的不是技术 而是思路。分成两步 根据文档定义 associate Pdu的数据结构,遵循上面说的原则 一个associate pdu有多个 pst Item,我们把pst Item定义为子项,然后serial()是associate pdu的网络序列化函数:

  1 public enum PDUTypes2     {3         AssociateRQ = 0x01,4         AssociateAC = 0x02,5         AssociateRJ = 0x03,6         DataTransfer = 0x04,7         AssociateReleaseRQ = 0x05,8         AssociateReleaseRP = 0x06,9         AssociateAbort = 0x07,10 11         ApplicationContext = 0x10,12         PresentationContext = 0x20,13         UserInformation = 0x50,14     }15 16     struct PDUAssociate {17         //header18         public byte pduType;19         public uint length;20         public ushort ProteocalVersion;21         public string CallEdAE ;//length=1622         public string CallingAE ;23 24         //1025         public byte appType;26         public ushort appLength;27         public string appName;28 29         //2030         public IList<PstItem> pstItems;31         //50 userinfo32         public byte userinfoType;33         public ushort userinfoLength;34         public byte maxnumType;35         public ushort maxnumLength;36         public uint maxnum;//DATA-TF PDU的可变字段的最大长度 一般为0x400 即102437         public byte impType;//关于实现类的38         public ushort impLength;39         public string impUID;40         public byte impVersionType;41         public ushort impVersionLength;42         public string impVersion;43 44         public byte[] serial()45         {46             if (length == 0)47                 return null;48             MemoryStream _stream = new MemoryStream((int)length + 6);49             WarpedStream stream = new WarpedStream(_stream);50             #region 序列化aassociateAC PDU51             //header52             stream.writeByte(pduType);53             stream.skip_write(1);54             stream.writeUint(length);//最低要94 我去 这是为什么呢55             stream.writeUshort(ProteocalVersion);56             stream.skip_write(2);57             stream.writeString(CallEdAE, 16);58             stream.writeString(CallingAE, 16);59             stream.skip_write(32);60 61             //1062             stream.writeByte(appType);63             stream.skip_write(1);64             stream.writeUshort(appLength);65             stream.writeString(appName, 0);66             //2167 68             for (int i = 0; i < pstItems.Count; i++)69             {70                 if (pstItems[i].used)71                 {72                     stream.writeByte(pstItems[i].pstType);73                     stream.skip_write(1);74                     stream.writeUshort(pstItems[i].pstLength);75                     stream.writeByte(pstItems[i].pstID);76                     stream.skip_write(3);77                     //if (pstItems[i].used)78                         //stream.writeBytes(new byte[] { 0x00, 0x00, 0x00 });79                     //else80                     //3081                     if (pstItems[i].pstType == 0x20)//如果是20则为printSCU82                     {83                         stream.writeByte(pstItems[i].absType);84                         stream.skip_write(1);85                         stream.writeUshort(pstItems[i].absLength);86                         stream.writeString(pstItems[i].absStr, 0);87                     }88 89                     stream.writeByte(pstItems[i].tsfType);90                     stream.skip_write(1);91                     if (pstItems[i].used)92                         stream.writeUshort(pstItems[i].tsfLeghth);93                     else94                         stream.writeUshort(0);95                     if (pstItems[i].used)96                         stream.writeString(pstItems[i].tsfStr, 0);97                 }98                 else99                 {
100                     stream.writeByte(pstItems[i].pstType);
101                     stream.skip_write(1);
102                     stream.writeUshort(0x08);
103                     stream.writeByte(pstItems[i].pstID);
104                     stream.writeBytes(new byte[] { 0x00, 0x04, 0x00 });
105                     stream.writeBytes(new byte[] { 0x40, 0x00, 0x00, 0x00 });
106                 }
107             }
108             
109 
110             //50
111             stream.writeByte(userinfoType);
112             stream.skip_write(1);
113             stream.writeUshort(userinfoLength);
114 
115             stream.writeByte(maxnumType);
116             stream.skip_write(1);
117             stream.writeUshort(maxnumLength);
118             stream.writeUint(maxnum);
119 
120             stream.writeByte(impType);
121             stream.skip_write(1);
122             stream.writeUshort(impLength);
123             stream.writeString(impUID, 0);
124 
125             stream.writeByte(impVersionType);
126             stream.skip_write(1);
127             stream.writeUshort(impVersionLength);
128             stream.writeString(impVersion, 0);
129             #endregion
130 
131             _stream.Flush();
132             byte[] data = _stream.GetBuffer();
133             stream.close();
134             _stream.Close();
135             return data;
136         }
137     }
138 
139     struct PstItem
140     {
141         //20 abstractsyntax transfersyntax传输语法 
142         public byte pstType;
143         public ushort pstLength;
144         public byte pstID;
145         public bool used;
146         //public byte pstRec; //保留字节 有效项是00 00 00 无效项是00 04 00
147         public byte absType;//20的子项 30 40 读取的时候应该跟20一并读出来
148         public ushort absLength;
149         public string absStr;
150         public byte tsfType; //传输语法项 本来也有多个 为了方便只写一个
151         public ushort tsfLeghth;
152         public string tsfStr;
153     }

构建一个associate-rq的pdu 并发送:

 1 public bool associateRQ()//请求建立连接2         {3             PDUAssociate pdu_ac = new PDUAssociate();4             #region 构造associateAC PDU5             //106             pdu_ac.appType = 0x10;7             pdu_ac.appLength = (ushort)UIDs.DICOMApplicationContextName.Length;//pdu_associate_rq.appLength8             pdu_ac.appName = UIDs.DICOMApplicationContextName;//pdu_associate_rq.appName9 
10             //20
11             //30 abs
12             //40 transfer syntax
13             pdu_ac.pstItems = new List<PstItem>();
14 
15             PstItem pst_ac = new PstItem();
16 
17             pst_ac.absType = 0x30;
18             pst_ac.absLength = (ushort)UIDs.Verification.Length;
19             pst_ac.absStr = UIDs.Verification;
20 
21             pst_ac.tsfType = 0x40;
22             pst_ac.tsfLeghth = (ushort)UIDs.ImplicitVRLittleEndian.Length;//pdu_associate_rq.pstItems[i].tsfLeghth; 
23             pst_ac.tsfStr = UIDs.ImplicitVRLittleEndian;//pdu_associate_rq.pstItems[i].tsfStr; 
24 
25             pst_ac.pstType = 0x20;
26             pst_ac.pstLength = (ushort)(4 + (4 + pst_ac.tsfLeghth) + (4 + pst_ac.absLength));
27             pst_ac.pstID = 0x01;//表达上下文ID,多个表达上下文的时候以作区分。这里我们为发送方 主动控制为01
28             pst_ac.used = true;
29             pdu_ac.pstItems.Add(pst_ac);
30 
31             //50
32             pdu_ac.userinfoType = 0x50;
33             pdu_ac.maxnumType = 0x51;
34             pdu_ac.maxnumLength = 0x04;
35             pdu_ac.maxnum = 0X4000;//16384
36 
37             pdu_ac.impType = 0x52;
38             pdu_ac.impLength = (ushort)UIDs.ImplementionUid.Length;
39             pdu_ac.impUID = UIDs.ImplementionUid;
40 
41             pdu_ac.impVersionType = 0x55;
42             pdu_ac.impVersionLength = 11;
43             pdu_ac.impVersion = "ASSASSMedic";
44 
45             pdu_ac.userinfoLength = (ushort)(4 * 3 + pdu_ac.maxnumLength + pdu_ac.impVersionLength + pdu_ac.impLength);
46 
47             //header
48             pdu_ac.pduType = 0x01;
49             pdu_ac.ProteocalVersion = 0x01;
50             pdu_ac.CallEdAE = calledAET;
51             pdu_ac.CallingAE = callingAET;
52             pdu_ac.length = (uint)((74 - 6) + (pdu_ac.appLength + 4) +
53                 (pdu_ac.userinfoLength + 4));
54 
55             for (int i = 0; i < pdu_ac.pstItems.Count; i++)
56             {
57                 if (pdu_ac.pstItems[i].used)
58                     pdu_ac.length += (ushort)(pdu_ac.pstItems[i].pstLength + 4);
59                 else
60                     pdu_ac.length += 12;
61             }
62 
63             #endregion
64             //序列化
65             stream.writeBytes(pdu_ac.serial());
66 
67             Console.WriteLine(string.Format("associate create success,CalledAET:{0}", calledAET));
68 
69             return false;
70         }

在这之前你还是得连接到SCP端:

 1 public void run(string ipStr, int port)2         {3             TcpClient _client = new TcpClient();4             IPAddress ipdrs = IPAddress.Parse(ipStr);5             _client.Connect(ipdrs, port);6 7             if (_client.Connected == false)8             {9                 Console.WriteLine("与所指定主机连接失败");
10                 Console.WriteLine("连接断开");
11                 return;
12             }
13             WrappedStream stream = new WrappedStream(_client.GetStream());
14 
15             echo(stream);
16             stream.close();
17             _client.Close();
18         }
19 
20         public void echo(WrappedStream _stream)
21         {
22             stream = _stream;
23             //第一步协商连接
24             associateRQ();
25             PDUTypes PduType = (PDUTypes)stream.readByte();
26             stream.skip(1);
27             uint pduLen = stream.readUint();
28             stream.skip((int)pduLen);
29             Console.WriteLine("协商连接成功");
30             //第二步 进行echo 请求
31             Verification_CECHORQ();
32             PduType = (PDUTypes)stream.readByte();
33             stream.skip(1);
34             pduLen = stream.readUint();
35             stream.skip((int)pduLen);
36             release();
37             Console.WriteLine("echo测试成功");
38         }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于WINCE6.0下载multiple XIP镜像文件

备注&#xff1a;基于usb下载的方式&#xff0c;MLC nand flash为K9G8G08U 1. Multiple XIP模式的文件说明 Multiple XIP模式下生成的文件有chain.bin、chain.lst、NK.bin、xip.bin和xipkernel.bin&#xff0c;如下图所示&#xff1a; 图1 2. Eboot下载Multiple XI…

Final Michael Scofield

转载于:https://www.cnblogs.com/andrewx/archive/2009/08/16/1547738.html

【转】dicom网络通讯入门(3)

转自&#xff1a; dicom网络通讯入门&#xff08;3&#xff09; - assassinx - 博客园 接下来可以进行消息传递了 &#xff0c;也就是dimse &#xff0c;再来复习下 什么是dimse 。n-set n-create c-echo 这些都是dimse 他们都是属于一种结构的pdu 那就是tf-pdu&#xff08;传…

【转】ubuntu 下 VNCview 远程桌面无法传输文件问题

转自&#xff1a;ubuntu18.04VNCview文件传输问题_gsls200808的专栏-CSDN博客_vnc传输文件按钮不可用 很多文章说VNCview不能传输文件&#xff0c;实际上这是一个误区。 以ubuntu为例&#xff0c;默认使用 sudo apt-get vnc4server 这个命令安装上的vncserver实际是tigerVNC…

在何时该用什么方式编译WinCE

这是一篇很好的文章&#xff0c;很多开发者其实并没有搞清楚这个问题&#xff1a;在何时该用什么方式编译WinCE 导致走了很多弯路&#xff0c;也包括我自己 感谢作者写了这篇文章 这么好的文章&#xff0c;我想应该翻译过来给大家 在何时该用什么方式编译WinCE 在新闻组里&…

【转】Ubuntu 16.04 安装 CUDA10.1 (解决循环登陆的问题)

转自&#xff1a;Ubuntu 16.04 安装 CUDA10.1 &#xff08;解决循环登陆的问题&#xff09; - Hongkai_Ding - 博客园 0. 前言 这里直接用 cuda安装文件同时安装 NVIDIA 驱动和 CUDA&#xff0c;没有单独安装更高版本的 NVIDIA 驱动&#xff1b;此安装是在 Intel 集显下的图形…

【转】Ubuntu 16.04 Nvidia驱动安装(run方式)

转自&#xff1a;Ubuntu 16.04 Nvidia驱动安装(run方式)_lihe的博客-CSDN博客 1.下载驱动程序 Nvidia驱动下载 https://www.geforce.cn/drivers/beta-legacy http://www.nvidia.cn/Download/index.aspx 根据显卡选择适用的驱动版本&#xff0c;下载完之后是一个名称为 NVIDIA…

2008R2Win7管理九DNS冗余和恢复

2008R2Win7管理九DNS冗余和恢复 本文介绍如何在ad中的主dns崩溃后快速利用备份dns将主dns恢复.以及在主dns完好的时候进行dns冗余备份 本文在file这台服务器上安装好dns角色来作为主dns的备份,安装角色的过程不在详细描述,略过.相信大家都知道了. 本问对拓扑图略有改动,故再次更…

【转】DICOM通讯(ACSE->DIMSE->Worklist)

转自&#xff1a;DICOM通讯&#xff08;ACSE-&#xff1e;DIMSE-&#xff1e;Worklist&#xff09; - 知乎 1 DICOM通讯概要介绍 DICOM通讯和TCP IP的设计原理别无二致。从用户数据到网络上传输数据的过程中&#xff0c;要经历多层协议处理&#xff0c;每经一层&#xff0c;就…

[Silverlight]16进制颜色转ARGB及Color转Int32

16进制色字符串转ARGB做Web的都喜欢用16进制字符串标识颜色&#xff0c;比如“#F5F5F5”这类的&#xff0c;自然这个到了Silverlight/WPF中就要用ARGB表示&#xff1a;“#FFF5F5F5”&#xff08;前两位标识Alpha&#xff0c;即透明度&#xff09;。这在XAML中没有任何问题&…

【转】DICOM通信 - PDU数据包(1)

转自&#xff1a;DICOM通信 - PDU数据包(1)_翼遥君的博客-CSDN博客 DICOM上层协议为DIMSE提供透明的网络数据传输服务&#xff0c;即以上层协议规定的协议数据单元传送接收DIMSE的命令流与数据流。上层协议中制定了上层服务以及协议数据单元(Protocol Data Unit&#xff0c;PD…

【转】DICOM通信 - PDU数据包(2)

转自&#xff1a;DICOM通信 - PDU数据包(2)_翼遥君的博客-CSDN博客 DICOM通信-PDU数据包(1)博客中主要分析了DICOM通信协议的连接协商请求和连接协商应答。 在7个PDU数据包中还有5个数据包&#xff0c;从结构上来看&#xff0c;他们比协议的协商过程要简单明了一些。 这篇博客…

“启动Word时提示出错,只能用安全模式才能打开”的解决方法

启动Word时提示出错&#xff0c;只能用安全模式才能打开&#xff0c;在这种模式下&#xff0c;一些功能将被禁用。而且&#xff0c;每次启动都会出现这样的提示信息&#xff0c;同事们很多向我求助。提示窗口如下&#xff1a; 于是我采取了以下一些措施&#xff1a; 1、用Offic…

DotNET内存管理与垃圾回收[转]

1. Stack&#xff08;栈&#xff09;和Heap&#xff08;堆&#xff09;每个线程对应一个stack&#xff0c;线程创建的时候CLR为其创建这个stack&#xff0c;stack主要作用是记录函数的执行情况。值类型变量&#xff08;函数的参数、局部变量等非成员变量&#xff09;都分配在st…

【转】WPF XAML X名称空间详解

转自&#xff1a;WPF XAML X名称空间详解_郎涯技术-CSDN博客 X名称空间里面的成员(如X:Name,X:Class)都是写给XAML编译器看的、用来引导XAML编译器将XAML代码编译为CLR代码。 【X名称空间里面到底都有些什么】 x名称空间映射的是:http://schemas.microsoft.com/winfx/2006/xam…

【转】DICOM医学图像处理:DICOM网络传输

背景&#xff1a; 专栏取名为DICOM医学图像处理原因是&#xff1a;博主是从医学图像处理算法研究时开始接触DICOM协议的。当初认识有局限性&#xff0c;认为DICOM只是一个简单的文件格式约定&#xff0c;简而言之&#xff0c;我当时认为DICOM协议就是扩展名为DCM文件的格式说明…

symbian c++ 开发环境Carbide.c++搭建

需要的东东&#xff1a;JDK1.3.1以上(自带了&#xff0c;不用安装)&#xff0c;SDK 2.0 以上&#xff08;CW版本&#xff09;&#xff0c;perl5.8.x carbide.c 免费版本 所以的东西都默认安装,下一步-->下一步,^_^! 现在开发用的最多的可能就是VC6了&#xff0c;听说以…

【转】9、XAML名称空间详解

转自&#xff1a;9、XAML名称空间详解 - 种花生的读书人 - 博客园 XAML命名空间 <Window xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"> </Window> xmlnshttp:…

wince使用自定义字体

在wince中使用自定义字体其实很简单&#xff0c;只要将自已的字体加到你的程序中就可以使用了&#xff0c;在使用完后再将其清除掉。以wince中加使用"Georgia”字体为例&#xff1a; 在程序初始化的时候将你的字体加载进程序 AddFontResource(L"NandFlash\\Font\\Geo…

【转】ubuntu 开机sudo启动应用程序

转自&#xff1a;ubuntu 开机sudo启动应用程序_Honhy的博客-CSDN博客_sudo 启动应用 第一步&#xff1a;创建一个脚本(run.sh)&#xff0c;我是放在桌面&#xff0c;脚本内容为: echo "hon123"|sudo -S /home/python/qt/Tools/QtCreator/bin/qtcreator exit 0 橙色的…