日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時(shí)間:2023/12/10 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】dicom网络通讯入门(2) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)自:dicom網(wǎng)絡(luò)通訊入門(2) - assassinx - 博客園

首先我們現(xiàn)一個(gè)echo響應(yīng)測試工具,也就是echo 的scu,不是實(shí)現(xiàn)打印作業(yè)管理么。同學(xué)我告訴你還早著呢。本來標(biāo)題取的就是《dicomviewer 第二彈 之 實(shí)現(xiàn)打印管理》名字多霸氣,最后我又改回來了。
?
首先你得把數(shù)據(jù)組織方式搞懂 那就是pdu? 和dimse? 元素? 數(shù)據(jù)元素。然后基于這之上你得把協(xié)商連接這塊搞懂 ,協(xié)商連接都沒通過不用說后面的了。然后你得把實(shí)現(xiàn)一個(gè)功能 比如打印 ,scu跟scp之間你來我往的 過程和概念搞懂 也就是dimse 然后才是服務(wù)類。最后你全都理解了 并且能跟醫(yī)院的設(shè)備正常 連接和操作了,那么恭喜你 差不多了。

最后要說的是: 解析dicom文件那篇你們都已經(jīng)看過了,dicom網(wǎng)絡(luò)通訊跟解析文件是一樣的 只不過解析的是socket數(shù)據(jù)流里的 元素 數(shù)據(jù)結(jié)構(gòu)本身是一樣的,然后他有一些規(guī)范和標(biāo)準(zhǔn) ,這就是dimse 和服務(wù)類 這些好像都在dicom標(biāo)準(zhǔn)的第四章? 第八章 第七章 。

?
實(shí)現(xiàn)這一大坨的東西 有點(diǎn)望而卻步了吧,其實(shí)總結(jié)起來就一句話 概括 按照dicom標(biāo)準(zhǔn) 封裝數(shù)據(jù) 處理數(shù)據(jù) ,然后根據(jù)特殊的參數(shù)和應(yīng)用場景 依規(guī)范響應(yīng)數(shù)據(jù),。
?
好廢話少說,開工 看過 標(biāo)準(zhǔn)簡介那篇博客 的都知道:
PDU是一種數(shù)據(jù)結(jié)構(gòu) dataElement是一種數(shù)據(jù)結(jié)構(gòu)
pdu結(jié)構(gòu)總共7種 其中用于連接控制的就占了6種
A-Associate-RQ PDU :連接請(qǐng)求協(xié)議數(shù)據(jù)單元,用于關(guān)聯(lián)請(qǐng)求。
A-Associate-AC PDU :連接接受協(xié)議數(shù)據(jù)單元,用于對(duì)關(guān)聯(lián)請(qǐng)求的應(yīng)答。
A-Associate-RJ? PDU :連接拒絕協(xié)議數(shù)據(jù)單元,用于拒絕關(guān)聯(lián)請(qǐng)求。

A-Release-RQ PDU? ?:連接釋放請(qǐng)求協(xié)議數(shù)據(jù)單元,
A-Release-RSP PDU :連接釋放響應(yīng)協(xié)議數(shù)據(jù)單元,
A-Abort PDU
P-DATA-TF PDU? ? ? ? :傳輸內(nèi)容的pdu(只有這一種)
當(dāng)通訊雙方建立了關(guān)聯(lián)之后,就可以使用P-DATA-TF所提供的傳輸服務(wù)來實(shí)現(xiàn)不同的通信功能了。
?總之你在進(jìn)行后面的dimse發(fā)送之前先得建立連接,否則你什么也搞不了。
好下面就協(xié)商連接的pdu進(jìn)行分析:

你問這圖是怎么來的 dicom 第八章 31頁。是不是跟上面說的是一樣的 開始兩字節(jié) 然后4字節(jié)表示長度,只不過這個(gè)更詳細(xì)了。協(xié)商連接pdu說起有6種 其實(shí)有好多是大同小異 比如Associate pdu, 他分rq 和ac? rq是請(qǐng)求 ac是響應(yīng)。
我把協(xié)商連接概述一下


概述之前,先解釋下什么是通訊:
還是炒剩飯 我又得把以前說過的話像背書一樣的背一遍了 其實(shí)他確實(shí)是那么回事。什么是通訊 :
命令tag +數(shù)據(jù)tag? 一起組成sop ,就好象說一句這樣的話:“把這根蘿卜拿去給我切了”??? “喏? ,蘿卜” 。其實(shí)這就是通訊? 跟人與人之間傳達(dá)意思一樣。說話的時(shí)候太熟練了? 沒察覺到? 你要仔細(xì)去想? 你自己是一臺(tái)電腦?? ,會(huì)是一個(gè)什么樣的步驟。
網(wǎng)絡(luò)傳輸 跟文件組織 是一樣的格式 。不過有命令tag 。很多命令tag組合到一堆,這稱之為dimse。 c-echo n-create c-find c-store 這些都稱之為dimse?。而打印管理是由很多組dimse 包括n-create 那些 你來我往的一套組成 比如 先n-create 什么東西 再 n-set什么東西,他有一種邏輯規(guī)范 什么參數(shù)錯(cuò)誤則不進(jìn)行n-set。這很多組dimse稱之為服務(wù)類 ,比如打印管理 就是一個(gè)服務(wù)類。這些規(guī)范在dicom標(biāo)準(zhǔn)的第八章有說明??傊赿imse傳遞之前 你必須得協(xié)商連接。
dicom標(biāo)準(zhǔn)的地址是這個(gè)Current Edition,英文的 ??匆脖容^困難 裝裝面子,主要是理解就行了 我看的也是別人翻譯的中文的。不過官方的就是官方的 沒辦法 某些地方你找不到原因 想?yún)⒄兆顦?biāo)準(zhǔn)的指示 你還是得硬著頭皮去看英文文檔。
??
Associate pdu 協(xié)商連接的過程:
又說多了 不論如何在進(jìn)行dimse之前必須得進(jìn)行連接協(xié)商 因?yàn)槟闩c別人進(jìn)行通訊首先你得確定兩個(gè)東西。 談話的主題是什么(虛擬語法),你是用哪國語言(傳輸語法)。這兩個(gè)東西一個(gè)稱之為虛擬語法 abstract syntax? 一個(gè)稱之為傳輸語法 transfersyntax 傳輸語法其實(shí)主要確定兩個(gè)東西 字節(jié)序 和 vr表示方式 ,如果你不知道字節(jié)序是什么 請(qǐng)自己百度 vr表示方式 跟文件解析一樣的,(字節(jié)序 和 vr表示方式?)他們兩個(gè)一起被稱之為表達(dá)上下文。注意表達(dá)上下文可以有多個(gè) 每個(gè)都有id。如果你是scp端 那么連接協(xié)商響應(yīng) 也就是association-ac的時(shí)候你要告知scu,本scp可以完成哪些表達(dá)上下文的服務(wù) 傳輸語法是什么,如果服務(wù)不了也要給出對(duì)應(yīng)的上下文id 并進(jìn)行告知。這樣的話scu端知道你服務(wù)不了就知難而退 主動(dòng)斷開連接。 其次還有些其他東西比如pdu最大數(shù)據(jù)長度 一般是0x4000。好了講完了 這就是協(xié)商連接的過程 對(duì)照上面的圖理解了否。
這是官方的解釋:

官方的解釋 網(wǎng)絡(luò)協(xié)議是分層的,Dicom ULP稱之為dicom上層協(xié)議。? 也就是上圖的dicom ul service provider。 反正要按照osi的標(biāo)準(zhǔn)來, 也就是說要定義一個(gè)associate-rq 或者 ac的數(shù)據(jù)結(jié)構(gòu)來,一切的數(shù)據(jù)序列化或者反序列化都由 dicom ul service provider 來進(jìn)行,反正只怕忽悠不死你。反正他說是那樣說 我們自己按照自己的方式來。
?
好終于要?jiǎng)哟a了 ,我喜歡的事情來了 噢啦啦啦。其實(shí)這是一個(gè)抽象化的過程,把你的想法付諸行動(dòng) 代碼化.就像某人說過的 主要的不是技術(shù) 而是思路。分成兩步 根據(jù)文檔定義 associate Pdu的數(shù)據(jù)結(jié)構(gòu),遵循上面說的原則 一個(gè)associate pdu有多個(gè)?pst Item,我們把pst Item定義為子項(xiàng),然后serial()是associate pdu的網(wǎng)絡(luò)序列化函數(shù):

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;//關(guān)于實(shí)現(xiàn)類的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; //保留字節(jié) 有效項(xiàng)是00 00 00 無效項(xiàng)是00 04 00 147 public byte absType;//20的子項(xiàng) 30 40 讀取的時(shí)候應(yīng)該跟20一并讀出來 148 public ushort absLength; 149 public string absStr; 150 public byte tsfType; //傳輸語法項(xiàng) 本來也有多個(gè) 為了方便只寫一個(gè) 151 public ushort tsfLeghth; 152 public string tsfStr; 153 }

構(gòu)建一個(gè)associate-rq的pdu 并發(fā)送:

1 public bool associateRQ()//請(qǐng)求建立連接2 {3 PDUAssociate pdu_ac = new PDUAssociate();4 #region 構(gòu)造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;//表達(dá)上下文ID,多個(gè)表達(dá)上下文的時(shí)候以作區(qū)分。這里我們?yōu)榘l(fā)送方 主動(dòng)控制為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("與所指定主機(jī)連接失敗"); 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 //第一步協(xié)商連接 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("協(xié)商連接成功"); 30 //第二步 進(jìn)行echo 請(qǐng)求 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 }

總結(jié)

以上是生活随笔為你收集整理的【转】dicom网络通讯入门(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。