1. 程式人生 > >DICOM醫學圖像處理:fo-dicom網絡傳輸之 C-Echo and C-Store

DICOM醫學圖像處理:fo-dicom網絡傳輸之 C-Echo and C-Store

通訊 過程 reading 網絡傳輸 基類 對象 last 控制流程 con

背景:

上一篇博文對DICOM中的網絡傳輸進行了介紹。主要參照DCMTK Wiki中的英文原文。通過對照DCMTK與fo-dicom兩個開源庫對DICOM標準的詳細實現,對理解DICOM標準有一個更直觀的認識。此篇博文是對上一篇博文的補充。由於專欄前面的演示樣例大多是利用DCMTK工具包來進行的,此次借著分析fo-dicom源代碼結構的機會,參照fo-dicom的README.md,給出C-ECHO 和C-STORE服務的詳細實現。在實現的同一時候給出DICOM3.0標準中的相關介紹,幫助我們理解。

C-ECHO的fo-dicom實現:

1)C-ECHO參數說明:

C-ECHO又叫驗證服務(即Verification),是用來驗證DICOM服務兩端的交流是否暢通。DICOM3.0的第7部分給出了C-ECHO服務的參數。例如以下圖1所看到的:

技術分享

【註意】:這裏解說一下DICOM3.0標準的閱讀方法。

以DICOM3.0標準的第7、8部分為例,【第7部分】中第9章開始解說DIMSE-C的各種服務。依次為C-STORE、C-FIND、C-GET、C-MOVE、C-ECHO(上圖1就是我在該部分的C-ECHO小節中截取的),當中前半部分主要給出了DIMSE-C各種服務的參數。這裏不過羅列出DICOM3.0標準的要求,目的是讓你明確各個服務參數是否是必要的(分別用M、U、=表示);後半部分開始解說DIMSE-C各種服務的協議及實現流程(即Protocol和Procedures)。在PROTOCOL中給出的是詳細的DIMSE-C服務的各種指令在傳輸過程中的格式,該部分也就是你利用抓包工具可以直接抓取的真實數據流;在Procedures中給出的是SCU和SCP之間的交互流程。通常為了說明服務是由誰發起的,由誰響應。在介紹Protocol的時候對於比較復雜的、可變的區域(Variables Fields)一般會放在附錄中。比如第7部分的附錄C和E等。【第8部分】與【第7部分】類似,從第7章開始介紹ACSE的各種服務的參數(例如以下圖2所看到的),依次為A-ASSOCIATE、A-RELEASE、A-ABORT、A-P-ABORT、P-DATA;第9章給出的是ACSE中各種服務的結構,即STRUCTURE。該部分與【第7部分】中的PROTOCOL相同,給出的是詳細ACSE PDU在傳輸時刻的數據格式,該部分也是能夠通過抓包工具直接獲得的;相同對於比較復雜的STRUCTURE介紹也會單獨放到附錄中,比如第8部分的附錄E。

技術分享

fo-dicom對於DIMSE消息的實現基類是DicomMessage。針對請求和響應分別派生出了DicomRequest和DicomResponse。最後依據不同的DIMSE服務派生對應的類。C-ECHO是當中最簡單的,fo-dicom已經給出了SCP和SCU的詳細實現。

參照fo-dicom中的README.md文件,給出C-ECHO SCP和SCU的代碼,詳情例如以下:

2)C-ECHO代碼實例:

C-ECHO SCP的代碼是直接利用了fo-dicom給出的DicomCEchoProvider類,通過創建DicomServer<DicomCEchoProvider>(12345)

對象,開啟C-ECHO SCP服務,當中參數12345表示C-ECHO服務的port號。C-ECHO SCU和C-ECHO SCP的代碼分別例如以下所看到的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dicom;
using Dicom.Network;

namespace CEchoSCU
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = new DicomClient();
            client.NegotiateAsyncOps();
            client.AddRequest(new DicomCEchoRequest());
            client.Send("127.0.0.1", 12345, false, "SCU", "ANY-SCP");
            Console.ReadLine();

        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Dicom;
using Dicom.Network;
namespace CEchoSCP
{
    class Program
    {
        static void Main(string[] args)
        {
            var server = new DicomServer<DicomCEchoProvider>(12345);
            Console.ReadLine();

        }
    }
}


實際執行結果例如以下:

技術分享

C-STORE的fo-dicom實現:

1)C-STORE參數說明:

C-STORE就是存儲服務。在醫療信息系統中最常見的服務之中的一個。尤其是PACS系統中。與C-ECHO服務同樣,DICOM3.0標準第7部分也給出了C-STORE服務的參數列表,例如以下圖4所看到的:

技術分享

該參數列表的目的相同是為了介紹C-STORE服務中各參數的必要性。真正的參數消息格式在興許的C-STORE PROTOCOL中介紹,例如以下圖5所看到的:

技術分享

圖5中給出的不過C-STORE RQ的實際消息格式,該消息由C-STORE服務的SCU(client)流向C-SOTRE服務的SCP(服務端);與之相相應的C-STORE-RSP消息是從SCP流向SCU。DICOM3.0標準中也有C-STORE-RSP的具體介紹,例如以下圖6所看到的。

技術分享

2)C-STORE代碼實例:

在fo-dicom的說明文檔README.md中僅僅給出了C-STORE的SCU演示樣例,例如以下圖7所看到的:

技術分享

上一篇博文對fo-dicom源代碼結構分析的基礎上可知。實現DIMSE眾多服務的SCU端非常easy,首先創建DicomClient實體類,代表一個client。然後通過AddRequest加入不同的請求就可以實現各種DIMSE的client,如圖7中C-STORE SCU的實現為:

client.AddRequest(new DicomCStoreRequest(@"test.dcm"));

DicomCStoreRequest類是DicomRequest的派生類,上述代碼通過制定DCM文件路徑來構建了一個DicomCStoreRequest對象,在DicomCStoreRequest內部通過打開指定的DCM文件提取獲得上述參數中的Affected SOP Instance UID等參數。

既然fo-dicom中沒有提供線程的C-STORE SCP實現,我們先利用DCMTK的storescp.exe工具來驗證一下fo-dicom給出的C-STORE SCU的正確性,測試代碼例如以下:

  • SCP端利用storescp.exe,在控制臺下輸入:storescp.exe –d –od c:\ 12345
  • SCU端利用fo-dicom中的C-STORE SCU。詳細代碼如上圖7所看到的。然後雙擊生成後的storescu.exe

最後能夠得到例如以下結果,如圖8所看到的:

技術分享

同一時候在C盤根文件夾下能夠看到被重命名的test.dcm文件,例如以下圖9所看到的:

技術分享

之所以被重命名我們在之前分析DCMTK開源庫源代碼時提到過,通常DCMTK會依據SOP Instance UID(-uf,默認的)對接收到的DCM文件進行重命名,當然也能夠通過選項設置重命名的方式。比如依照時間(-tn)、特定前綴(-fe)等等,例如以下圖10所看到的。

技術分享

由此說明fo-dicom中給出的C-STORE SCU功能正常。接下來我們嘗試利用fo-dicom構建C-STORE SCP。

3)構建C-STORE SCP

打開C-ECHO SCP的實現DicomCEchoProvider.cs文件,我們看到DicomCEchoProvider類通過派生DicomService服務類來實現了Dicom服務的基本框架。然後通過實現IDicomServiceProvider和IDicomCEchoProvider接口,完畢了C-ECHO 的服務端。細致查看DicomCEchoProvider的代碼能夠發現,事實上就是在接收到A-ASSOCIATE-RQ消息後。判別Presentation Context中的Abstract Syntax。依據實際請求消息來決定是否建立連接,另外當接收到C-ECHO SCU發起的C-ECHO Request時,向其會送DicomCEchoResponse確認信息就可以。

既然通過實現兩個接口函數就能夠完畢C-ECHO SCP的構建,那麽我們就自己嘗試來完畢C-STORE SCP的搭建,仿照DicomCEchoProvider的方式,DicomCStoreProvider的代碼例如以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dicom;
using Dicom.Log;
using Dicom.Network;
using System.Threading;
using System.IO;
namespace CStoreSCP
{
    class CStoreSCPProvider : DicomService, IDicomServiceProvider, IDicomCStoreProvider
    {
        public CStoreSCPProvider(Stream stream, Logger log) : base(stream, log) { }

        public DicomCStoreResponse OnCStoreRequest(DicomCStoreRequest request)
        {
            return new DicomCStoreResponse(request,DicomStatus.Success);
        }
        public void OnCStoreRequestException(string tempFileName, Exception e)
        {

        }
        public void OnReceiveAssociationRequest(DicomAssociation association)
        {
            foreach (var pc in association.PresentationContexts)
            {
                if (pc.AbstractSyntax == DicomUID.Verification)
                    pc.SetResult(DicomPresentationContextResult.Accept);
                else
                {
                    //pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
                }
                if (pc.AbstractSyntax == DicomUID.CTImageStorage)
                {
                    pc.SetResult(DicomPresentationContextResult.Accept);
                }
            }
            SendAssociationAccept(association);

        }
        public void OnReceiveAssociationReleaseRequest()
        {
            SendAssociationReleaseResponse();

        }
        public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
        {

        }
        public void OnConnectionClosed(int errorCode)
        {

        }


    }
}

然後通過var server = new DicomServer<CStoreSCPProvider>(12345);Console.ReadLine(); 來構建一個C-STORE SCP應用。

下圖11是先執行CStoreSCP.exe,然後執行CStoreSCU.exe得到的結果:

技術分享

從圖11的輸出結果能夠看出。此次C-STORE SCP和SCU兩端的通訊順利完畢,那麽我們發送的C:\test.dcm文件會被CStoreSCP.exe存儲到那裏呢?由上一篇博文分析我們知道fo-dicom庫中將DICOM的服務基本框架放在了DicomService類中。查看當中處理P-DATA服務的核心函數ProcessPDataTF,能夠看到例如以下代碼:

var file = new DicomFile(); 
file.FileMetaInfo.MediaStorageSOPClassUID = pc.AbstractSyntax; 
file.FileMetaInfo.MediaStorageSOPInstanceUID = _dimse.Command.Get<DicomUID>(DicomTag.AffectedSOPInstanceUID); 
file.FileMetaInfo.TransferSyntax = pc.AcceptedTransferSyntax; 
file.FileMetaInfo.ImplementationClassUID = Association.RemoteImplemetationClassUID; 
file.FileMetaInfo.ImplementationVersionName = Association.RemoteImplementationVersion; 
file.FileMetaInfo.SourceApplicationEntityTitle = Association.CallingAE;
_dimseStream = CreateCStoreReceiveStream(file);

轉到CreateCStoreReceiveStream函數內部,通過函數的說明就能夠知道fo-dicom對C-STORE服務默認情況下是在系統中創建了一個暫時文件,用來接收C-STORE SCU的數據。因此能夠判斷我們的test.dcm文件應該也在暫時目錄中,打開我本機的temp目錄。能夠看到有一個後綴為tmp的暫時文件。例如以下圖12所看到的。

文件大小與我們測試用的test.dcm同樣。嘗試改動.tmp的擴展名,改動後能夠使用DICOM Viewer軟件正常打開。因此說明我們的C-STORE SCP順利成功。

技術分享

DICOM數據流分析:

C-ECHO服務數據流分析:

1)工具:

在本地測試。為了抓取127.0.0.1回路數據包,須要使用RawCap.exe工具包。

RawCap.exe是控制臺程序,在抓取本地回路數據包時非常便捷。

當抓取完畢後我們須要借助於WireShark的強大分析功能,來實現C-ECHO數據流的具體分析。WireShark能夠直接打開RawCap.exe抓取的.pcap數據包。

WireShark是功能強大的數據包統計分析工具。當然本身也能夠抓取網絡數據包(本地回路數據包不方便)。WireShark支持眾多協議,當中包含DICOM協議。以下以C-ECHO的數據包為例,簡介一下怎樣使用WireShark來自己主動識別並解析DICOM數據包。首先打開抓取的本地C-ECHO數據包cecho.pcap。如圖13,在Protocol中右鍵選擇"Protocol Preferences “中的"Data Preferences…”,會彈出一個協議設置窗體如圖13。在左側列表中找到DICOM協議,勾選圖14中紅色部分。該部分的意思是除了檢測DICOM協議默認port104的數據包的同一時候也檢測其它port的數據包。之所以須要選擇此項是由於非常多DICOM服務並未使用協議默認的104port。設置完畢後,又一次查看Protocol列,能夠看到出現了DICOM字樣。如圖15所看到的。最上方的帶DICOM字樣的數據包就是我們抓取到的C-ECHO服務的本地回路數據包。

技術分享

技術分享

技術分享

2)C-ECHO數據流分析:

利用RawCap.exe和WireShark兩大強大的工具,我們已經能夠直觀的看到抓取的DICOM數據包了。接下來我就依照DICOM標準第7部分和第8部分中的內容,逐個數據包來分析一下,通過觀察真實的數據包來加深一下對DICOM協議的理解。

從圖15中能夠看到。最頂部DICOM協議包括6個數據包,各自是連接建立(A-ASSOCIATE RQ/A-ASSOCIATE AC)、數據交互(P-DATA-TF)、連接釋放(A-RELEASE RQ/A-RELEASE RP),這與DICOM協議第8部分中介紹的ACSE控制流程相符。

A-ASSOCIATE RQ/A-ASSOCATE AC分析:

雙擊第一個DICOM數據包,該數據包是A-ASSOCIATE RQ的真實數據流,如圖16所看到的:

技術分享

依照DICOM協議第8部分中第9章對A-ASSOCIATE RQ PDU的描寫敘述。我們來逐項對照(DICOM協議可參照圖17):第一項1個字節的PDU-type,圖中為01H,說明該數據包代表的是A-ASSOCIATE RQ;第二項一個字節的保留,數據流為00H;第三項是四個字節的PDU-length,圖中為00 00 00 ff,轉換為無符號整數正好為255。這也是整個圖中藍色部分興許的數據包長度;第四項是兩個字節的Protocol-Version,圖中為00 01。相應版本號為1;第五項為兩字節保留;第六項和第七項是我們熟悉的AE Title,從WireShark的數據流中也能夠看出各自是ANY-SCP和ECHOSCU;第8項又是一堆保留字節。用00H填充;第9項是一個可變區域(Variable Fields),該項是復合項。內部包括多個獨立的子項。由圖16能夠看出該復合項內部含有Application ContextPresentation Context(2個,ID各自是1、3)、UserInfo三個子項;而UserInfo又是一個復合項,其內部又包括了Max PDU LengthImplentationUIDImplentationVersion三個子項。從WireShark的分析來看,Application Context子項類型為10H、Presentation Context子項類型為20H、UserInfo子項為50H(其內部的嵌套子項的類型分別為,Max PDU Length-51H、Implentation UID-52H、Implentation Version-55H)。各個子項的類型與DICOM協議第7、8兩部分中的附錄D相相應。比如圖19中我截取的是Max PDU Length子項的格式。A-ASSOCIATE AC的數據包分析與A-ASSOCIATE RQ類似,僅僅是A-ASSOCIATE AC的數據流更簡單一些,這裏就不做具體介紹了。(終於數據域DICOM協議的相應結果如圖18)。

技術分享

技術分享

技術分享

A-RELEASE RQ/A-RELEASE RP分析:

連接釋放的數據包格式簡單,以下圖20和圖21各自是DICOM協議第8部分中給出的連接釋放請求和應答數據包的格式:

技術分享

技術分享

雙擊WireShark中的連接釋放數據包,能夠看到兩者的數據包類型分別為05H和06H,這與上圖中DICOM協議的規定全然一致。

技術分享

P-DATA-TF:

在上一篇博文中(http://blog.csdn.net/zssureqh/article/details/41016091)我已經分析了。DICOM協議第7部分中規定的DIMSE消息(Command和Dataset)是通過第8部分中ACSE協議中的P-DATA-TF服務以PDV的形式來傳輸的

以下就讓我們來分析一下DIMSE消息中C-ECHO RQ 和C-ECHO RSP的格式:

雙擊WireShark數據包中間兩個,從數據流向能夠斷定一個是C-ECHO RQ消息。一個是C-ECHO RSP消息。

先打開第一個。依照上一篇博文的分析。首先該數據包是一個P-DATA-TF PDU,因此須要符合下圖23中的格式。

技術分享

通過分析最外層的是代表P-DATA-TF類型的04H。然後是由DIMSE消息填充的PDV區域,該項是復合項,第一子項是Item-length,此處為46H;第二子項為Presentation-context-ID,此處為01H;第三子項又是一個復合項。是DICOM標準第4部分中給出的DIMSE消息結構。包含Message Control Header、Command和DataSet三部分。此處的MessageControlHeader為03H,即表示是Command數據而不是DataSet,且是最後一個PDV,即Last Fragment。詳細的相應關系如圖24所看到的:

技術分享

C-STORE服務數據流分析:

1)工具:

依舊使用RawCap.exe+WireShark來解決。

2)C-STORE數據流分析:

依照C-ECHO中的分析方式,相同能夠看到DICOM數據包,如圖25所看到的:

技術分享

A-ASSOCIATE RQ/A-ASSOCIATE AC:

對於A-ASSOCIATE RQ/A-ASSOCIATE AC的分析與C-ECHO中基本類似,唯一不同的就是對於C-STORE服務須要不同的Presentation Context描寫敘述上下文,如圖26所看到的,此處C-STORE須要的是CT Image Storage服務,其SOP Class UID為1.2.840.10008.5.1.4.1.1.2。

技術分享

A-RELEASE RQ/A-RELEASE RP:

與C-ECHO中的同樣,這也說明了博文中的C-ECHO 和C-STORE服務實現成功。連接可以正常釋放。

P-DATA-TF:

此處著重分析一下C-STORE服務中的P-DATA-TF數據包,由於傳輸一個DCM文件須要多個PDU,自然也須要多個PDV。

所以我們通過分析C-STORE的P-DATA-TF數據包能夠更形象的學習Message Control Header和DIMSE的知識

相同傳輸的每一個數據包首先符合P-DATA-TF的格式要求。第一項是PDU類型,即04H。隨後是保留項、PDU-length、PDV復合項……,這與C-ECHO中的分析相同。依照上一篇博文的分析,C-STORE PROTOCOL的流程是CSTORE SCU向SCP發送C-STORE RQ消息。可是打開圖中的第一個P-DATA數據包時我們看到的卻不是C-STORE RQ,而是當中的一個數據片段,例如以下圖27所看到的。

技術分享

依次查看後面的幾個P-DATA數據包,都是類似的情況。最後倒數兩個各自是C-STORE RQ中DCM文件數據的最後一個數據包(Last Fragment)和SCP向SCU發送的C-STORE RSP,詳細分析如圖28所看到的:

技術分享

從最後數據包Command中的(0000,0100)的值域8001H可知該指令就是C-STORE RSP。

看到這裏你也許會非常興奮,由於我們最終也看到了C-STORE服務的真實數據流。可是在上圖中的全部DICOM相應的數據包中我們並未找到C-STORE SCU發起的C-STORE RQ數據包,那麽C-STORE RQ數據包在哪裏呢

讓我們將cstore.pcap的全部數據包依照時間排序,出現了大量標記為[TCP segment of a reassembled PDU]的TCP數據包。

技術分享

打開第一個標記為[TCP segment of a reassembled PDU]的TCP數據包。其內部的真實數據分析例如以下圖30所看到的:

技術分享

至此我們順利找到了C-STORE SCU端發送的C-STORE RQ消息,之所以沒有在WireShark中以DICOM協議顯示,可能是因為WireShark在識別多個連續分片的數據時不夠智能。博文中的演示樣例圖和文字較多,細致閱讀後應該對DICOM3.0中的協議會有更進一步的了解。通過分析數據包的方式在更直觀的學習和掌握DICOM3.0標準的同一時候,對後期排查DICOM網絡傳輸相關錯誤也會有幫助。

備註:

再次說明一下閱讀DICOM3.0標準的方式:

以DICOM3.0標準的第7、8部分為例,【第7部分】中第9章開始解說DIMSE-C的各種服務,依次為C-STORE、C-FIND、C-GET、C-MOVE、C-ECHO(上圖1就是我在該部分的C-ECHO小節中截取的),當中前半部分主要給出了DIMSE-C各種服務的參數,這裏不過羅列出DICOM3.0標準的要求。目的是讓你明確各個服務參數是否是必要的(分別用M、U、=表示);後半部分開始解說DIMSE-C各種服務的協議及實現流程(即Protocol和Procedures)。在PROTOCOL中給出的是詳細的DIMSE-C服務的各種指令在傳輸過程中的格式,該部分也就是你利用抓包工具可以直接抓取的真實數據流;在Procedures中給出的是SCU和SCP之間的交互流程。通常為了說明服務是由誰發起的。由誰響應。在介紹Protocol的時候對於比較復雜的、可變的區域(Variables Fields)一般會放在附錄中。比如第7部分的附錄C和E等;【第8部分】與【第7部分】類似。從第7章開始介紹ACSE的各種服務的參數(如圖2所看到的),依次為A-ASSOCIATE、A-RELEASE、A-ABORT、A-P-ABORT、P-DATA;第9章給出的是ACSE中各種服務的結構,即STRUCTURE,該部分與【第7部分】中的PROTOCOL相同,給出的是詳細ACSE PDU在傳輸時刻的數據格式,該部分也是能夠通過抓包工具直接獲得的。相同對於比較復雜的STRUCTURE介紹也會單獨放到附錄中。比如第8部分的附錄E。

實例project及抓取的數據包:

代碼:搜索我上傳的資源

數據包:搜索我上傳的資源

興許專欄博文介紹:

利用PHP Skel結合DCMTK開發WEB PACS應用

利用oracle直接操作DICOM數據

C#的異步編程模式在fo-dicom中的應用

VMWare三種網絡連接模式的實際測試



作者:[email protected]

時間:2014-11-18

DICOM醫學圖像處理:fo-dicom網絡傳輸之 C-Echo and C-Store