程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> dicom網絡通訊入門(2)

dicom網絡通訊入門(2)

編輯:C#入門知識

第二篇,前面都是閒扯 其實正文現在才開始,這次是把壓箱底的東西都拿出來了。 首先我們今天要干的事是實現一個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
基於DICOM標准的醫學圖像通信過程的實現
連接接受協議數據單元,
A.Associate—RJ PDU
連接拒絕協議數據單元,
A-Release-RQ PDU
用於對關聯請求的應答。
用於拒絕關聯請求。
連接釋放請求協議數據單元,
A-Release.RSP PDU
連接釋放響應協議數據單元,
A.Abort PDU
傳輸內容的pdu只有一種P.DATA.TF PDU,
當通訊雙方建立了關聯之後,就可以使用P.DATA-TF所提供的傳輸服務來實現不同的通信功能了。
 總之你在進行後面的dimse發送之前先得建立連接,否則你什麼也搞不了。
好下面就協商連接的pdu進行分析:

你問這圖是怎麼來的 dicom 第八章 31頁。是不是跟上面說的是一樣的 開始兩字節 然後4字節表示長度,只不過這個更詳細了。協商連接pdu說起有6種 其實有好多是大同小異 比如Associate pdu, 他分rq 和ac  rq是請求 ac是響應。
我把協商連接概述一下


概述之前,什麼是通訊:
還是炒剩飯 我又得把以前說過的話像背書一樣的背一遍了 其實他確實是那麼回事。什麼是通訊 :
命令tag +數據tag  一起組成sop ,就好象說一句這樣的話:“把這根蘿卜拿去給我切了”    “喏  ,蘿卜” 。其實這就是通訊  跟人與人之間傳達意思一樣。說話的時候太熟練了  沒察覺到  你要仔細去想  你自己是一台電腦   ,會是一個什麼樣的步驟。
網絡傳輸 跟文件組織 是一樣的格式 。不過有命令tag 。很多命令tag組合到一堆這稱之為dimse。 echo n-create c-find c-store 這些都稱之為dimse ,記住沒有c-print  大哥
打印管理是由很多組dimse 包括n-create 那些 你來我往的一套組成 比如 先n-create 什麼東西 再 n-set什麼東西,他有一種邏輯規范 什麼參數錯誤則不進行n-set。這很多組dimse稱之為服務類 ,比如打印管理 就是一個服務類。這些規范在dicom標准的第八章有說明。總之在dimse傳遞之前 你必須得協商連接。
dicom標准的地址是這個http://medical.nema.org/standard.html,英文的 。看也比較困難 裝裝面子,主要是理解就行了 我看的也是別人翻譯的中文的。不過官方的就是官方的 沒辦法 某些地方你找不到原因 想參照最標准的指示 你還是得硬著頭皮去看英文文檔。
  
Associate pdu 協商連接的過程:
又說多了 不論如何在進行dimse之前必須得進行連接協商 因為你與別人進行通訊首先你得確定幾個東西。 ,談話的主題是什麼,你是用哪國語言。這兩個東西一個稱之為虛擬語法 abssyntax  一個稱之為傳輸語法 transfersyntax 傳輸語法其實主要確定兩個東西 字節序 和 vr表示方式 ,如果你不知道字節序是什麼 請自己百度 vr表示方式 跟文件解析一樣的,他們兩個一起被稱之為表達上下文。注意表達上下文有多個 每個都有id。如果你是scp端 那麼連接協商響應 也就是association-ac的時候你要告知 以你scp程序的服務能力可以完成哪些表達上下文的服務 傳輸語法語法是什麼,如果服務不了也要給出對應的上下文id 並進行告知。這樣的話scu端知道你服務不了就知難而退 主動斷開連接。 其次還有些其他東西比如pdu最大數據長度 一般是0x4000。好了講完了 這就是協商連接的過程 對照上面的圖理解了否。
這是官方的解釋:

官方的解釋 網絡協議是分層的,Dicom ul p ,稱之為dicom上層協議。  也就是上圖的dicom ul service provider。 反正要按照osi的標准來, 也就是說要定義一個associate-rq 或者 ac的數據結構來,一切的數據序列化或者反序列化都由 dicom ul service provider 來進行,反正只怕忽悠不死你。反正他說是那樣說 我們自己按照自己的方式來。
 
好終於要動代碼了 ,我喜歡的事情來了 噢啦啦啦。其實這是一個抽象化的過程,把你的想法付諸行動 代碼化.就像某人說過的 主要的不是技術 而是思路。分成兩步 根據文檔定義 associate Pdu的數據結構,遵循上面說的原則 一個associate pdu有多高 pst Item,我們把pst Item定義為子項,然後serial()是associate pdu的網絡序列化函數:

            AssociateRQ =          AssociateAC =          AssociateRJ =          DataTransfer =          AssociateReleaseRQ =          AssociateReleaseRP =          AssociateAbort =  
         ApplicationContext =          PresentationContext =          UserInformation =   
              
                                         CallEdAE ;
           
         
                               
         
          IList<PstItem>         
                                                   maxnum;
           impType;
                                                   
                         (length ==                                MemoryStream _stream =  MemoryStream(()length +              WarpedStream stream =               序列化aassociateAC PDU
             
              stream.skip_write(             stream.writeUint(length);
              stream.skip_write(             stream.writeString(CallEdAE,              stream.writeString(CallingAE,              stream.skip_write( 
             
              stream.skip_write(              stream.writeString(appName,              
 
              ( i = ; i < pstItems.Count; i++                                         stream.skip_write(                       stream.skip_write(                                                                                        
                      (pstItems[i].pstType == )
                           stream.skip_write(                          stream.writeString(pstItems[i].absStr,   
                      stream.skip_write(                                           
                         stream.writeUshort(                                              stream.writeString(pstItems[i].tsfStr,                   
                       stream.skip_write(                     stream.writeUshort(                      stream.writeBytes( [] { , ,                      stream.writeBytes( [] { , , ,                
 
             
              stream.skip_write(  
              stream.skip_write(   
              stream.skip_write(              stream.writeString(impUID,  
              stream.skip_write(              stream.writeString(impVersion,              
 
              [] data =                  
               
                                                 
           absType;
                               tsfType; 
                     

構建一個associate-rq的pdu 並發送:

   associateRQ()
              PDUAssociate pdu_ac =               構造associateAC PDU
             
             pdu_ac.appType =              pdu_ac.appLength = ()UIDs.DICOMApplicationContextName.Length;
             pdu_ac.appName = UIDs.DICOMApplicationContextName; 
                                       
             pdu_ac.pstItems =  List<PstItem> 
             PstItem pst_ac =  
             pst_ac.absType =              pst_ac.absLength = (             pst_ac.absStr = 
             pst_ac.tsfType =              pst_ac.tsfLeghth = ()UIDs.ImplicitVRLittleEndian.Length;
             pst_ac.tsfStr = UIDs.ImplicitVRLittleEndian;
 
             pst_ac.pstType =              pst_ac.pstLength = ()( + ( + pst_ac.tsfLeghth) + ( +             pst_ac.pstID = ;
             pst_ac.used =   
             
             pdu_ac.userinfoType =              pdu_ac.maxnumType =              pdu_ac.maxnumLength =              pdu_ac.maxnum = ;
 
             pdu_ac.impType =              pdu_ac.impLength = (             pdu_ac.impUID = 
             pdu_ac.impVersionType =              pdu_ac.impVersionLength =              pdu_ac.impVersion =  
             pdu_ac.userinfoLength = ()( *  + pdu_ac.maxnumLength + pdu_ac.impVersionLength + 
             
             pdu_ac.pduType =              pdu_ac.ProteocalVersion =              pdu_ac.CallEdAE =             pdu_ac.CallingAE =             pdu_ac.length = ()(( - ) + (pdu_ac.appLength + ) +
                 (pdu_ac.userinfoLength +  
              ( i = ; i < pdu_ac.pstItems.Count; i++                                       pdu_ac.length += ()(pdu_ac.pstItems[i].pstLength +                  
                     pdu_ac.length +=   
             
             
  
             Console.WriteLine(.Format( 
                       }

在這之前你還是得連接到SCP端:

   run( ipStr,               TcpClient _client =              IPAddress ipdrs =  
              (_client.Connected ==                   Console.WriteLine(                 Console.WriteLine(                               WarpedStream stream =  
     
                        stream =             
              PDUTypes PduType =             stream.skip(              pduLen =             stream.skip((             Console.WriteLine(             
              PduType =             stream.skip(             pduLen =             stream.skip((              Console.WriteLine(         }

注意我們並沒有對收到的associate-ac數據進行解碼驗證,直接偷懶略過了 。我們默認對方都是好人 都是按照套路來的 並且能夠承擔我們所請求的echo服務。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved