在Delphi 7 中用Indy開發Socket應用程序(上)

筆者在前一段的工作中,需要開發一套簡單的網絡數據傳輸程序。由於平時常用Delphi做點開發,故此次也不例外。Delphi 7中帶有兩套TCP Socket組件:Indy Socket組件(IdTCPClient和IdTCPServer)和Delphi原生的TCP Socket組件(ClientSocket和ServerSocket)。但是,Borland已宣稱ClientSocket和ServerSocket組件即將被廢棄,建議用相應的Indy組件來代替。因此,筆者使用了Indy。本文在對Indy進行簡要介紹的基礎上,創建了一組簡單的TCP Socket數據傳輸應用來演示了Indy的使用方法。

 

開放源代碼的Internet組件集——Internet Direct(Indy)

Internet Direct(Indy)是一組開放源代碼的Internet組件,涵蓋了幾乎所有流行的Internet協議。Indy用Delphi編寫,被包含在Delphi 6,Kylix 1和C++ Builder 6及以上各個版本的Borland開發環境中。Indy曾經叫做WinShoes(雙關於WinSock——Windows的Socket庫),是由Chad Z. Hower領導的一羣開發者構建的,可以從Indy的站點www.nevrona.com/indy上找到更多的信息並下載其新版本。到筆者撰寫本文時爲止,Indy的最新穩定版是9.0.14,Indy 10也進入了Beta測試階段。

Delphi 7中所帶的是Indy 9。在其的組件面板上,一共安裝有100多個Indy組件。使用這些組件你可以開發基於各種協議的TCP客戶和服務器應用程序,並處理相關的編碼和安全問題。你可以通過前綴Id來識別Indy組件。

Indy是阻塞式(Blocking)的

當你使用Winsock開發網絡應用程序時,從Socket中讀取數據或者向Socket寫入數據都是異步發生的,這樣就不會阻斷程序中其它代碼的執行。在收到數據時,Winsock會嚮應用程序發送相應的消息。這種訪問方式被稱作非阻塞式連接,它要求你對事件作出響應,設置狀態機,並通常還需要一個等待循環。

與通常的Winsock編程方法不同的是,Indy使用了阻塞式Socket調用方式。阻塞式訪問更像是文件存取。當你讀取數據,或是寫入數據時,讀取和寫入函數將一直等到相應的操作完成後才返回。比如說,發起網絡連接只需調用Connect方法並等待它返回,如果該方法執行成功,在結束時就直接返回,如果未能成功執行,則會拋出相應的異常。同文件訪問不同的是,Socket調用可能會需要更長的時間,因爲要讀寫的數據可能不會立即就能準備好(在很大程度上依賴於網絡帶寬)。

阻塞式Socket並非惡魔(Evil)

長期以來,阻塞式Socket都遭到了毫無理由的攻擊。其實阻塞式Socket並非如通常所說的那樣可怕。這還要從Winsock的發展說起。

當Socket被從Unix移植到Windows時,一個嚴重的問題立即就出現了。Unix支持fork,客戶程序和服務器都能夠fork新的進程,並啓動這些進程,從而能夠很方便地使用阻塞式Socket。而Windows 3.x既不支持fork也不支持多線程,當使用阻塞式Socket時,用戶界面就會被“鎖住”而無法響應用戶輸入。

爲克服Windows 3.x的這一缺陷,微軟在Winsock中加入了異步擴展,以使Winsock不會“鎖住”應用程序的主線程(也是唯一的線程)。然而,這需要了一種完全不同的編程方式。於是有些人爲了掩飾這一弱點,就開始強烈地誹謗阻塞式Socket。

當Win32出現的時候,它能夠很好地支持線程。但是既成的觀念已經很難更改,並且說出去的話也無法收回,因此對阻塞式Socket的誹謗繼續存在着。

事實上,阻塞式Socket仍然是Unix實現Socket的唯一方式,並且它工作得很好。

阻塞式Socket的優點

歸結起來,在Windows上使用阻塞式Socket開發應用程序具有如下優點:

○ 編程簡單——阻塞式Socket應用程序很容易編寫。所有的用戶代碼都寫在同一個地方,並且順序執行。

○ 容易向Unix移植——由於Unix也使用阻塞式Socket,編寫可移植的代碼就變得比較容易。Indy就是利用這一點來實現其多平臺支持而又單一源代碼的設計。

○ 很好地利用了線程技術——阻塞式Socket是順序執行的,其固有的封裝特性使得它能夠很容易地使用到線程中。

阻塞式Socket的弱點

事物都具有兩面性,阻塞式Socket也不例外。它的一個主要的缺點就是使客戶程序的用戶界面“凍結”。當在程序的主線程中進行阻塞式Socket調用時,由於要等待Socket調用完成並返回,這段時間就不能處理用戶界面消息,使得Update、Repaint以及其它消息得不到及時響應,從而導致用戶界面被“凍結”。

使用TIdAntiFreeze對抗“凍結”

Indy使用一個特殊的組件TIdAntiFreeze來透明地解決客戶程序用戶界面“凍結”的問題。TIdAntiFreeze在Indy內部定時中斷對棧的調用,並在中斷期間調用Application.ProcessMessages方法處理消息,而外部的Indy調用繼續保存阻塞狀態,就好像TIdAntiFreeze對象不存在一樣。你只要在程序中的任意地方添加一個TIdAntiFreeze對象,就能在客戶程序中利用到阻塞式Socket的所有優點而避開它的一些顯著缺點。

Indy使用了線程技術

阻塞式Socekt通常都採用線程技術,Indy也是如此。從最底層開始,Indy的設計都是線程化的。因此用Indy創建服務器和客戶程序跟在Unix下十分相似,並且Delphi的快速開發環境和Indy對WinSock的良好封裝使得應用程序創建更加容易。

Indy服務器模型

一個典型的Unix服務器有一個或多個監聽進程,它們不停地監聽進入的客戶連接請求。對於每一個需要服務的客戶,都fork一個新進程來處理該客戶的所有事務。這樣一個進程只處理一個客戶連接,編程就變得十分容易。

Indy服務器工作原理同Unix服務器十分類似,只是Windows不像Unix那樣支持fork,而是支持線程,因此Indy服務器爲每一個客戶連接分配一個線程。

圖1顯示了Indy服務器的工作原理。Indy服務器組件創建一個同應用程序主線程分離的監聽線程來監聽客戶連接請求,對於接受的每一個客戶,都創建一個新的線程來爲該客戶提供服務,所有與這一客戶相關的事務都由該線程來處理。

使用組件TIdThreadMgrPool,Indy還支持線程池。

 

 

 

圖1 Indy服務器工作原理

 

線程與Indy客戶程序

Indy客戶端組件並未使用線程。但是在一些高級的客戶程序中,程序員可以在自定義的線程中使用Indy客戶端組件,以使用戶界面更加友好。

簡單的Indy應用示例

下面將創建一個簡單的TCP客戶程序和一個簡單的TCP服務器來演示Indy的基本使用方法。客戶程序使用TCP協議同服務器連接,並向服務器發送用戶所輸入數據。服務器支持兩條命令:DATA和QUIT。在DATA命令後跟隨要發送的數據,並用空格將命令字DATA和數據分隔開。

表單佈局

建立一個項目組,添加一個客戶程序項目和一個服務器項目。客戶程序和服務器程序的表單佈局如同2和圖3所示。客戶程序表單上放置了TIdTCPClient組件,服務器程序表單上放置了TIdTCPServer組件。爲防止客戶程序“凍結”,還在其表單上放置TIdAntiFreeze組件。

客戶程序和服務器程序的表單上都放置有TListBox組件,用來顯示通信記錄。

 

 

 

圖2 簡單的TCP客戶程序表單

 

 

 

 

圖3 簡單的TCP服務器程序表單
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章