一、Socket通信:
Delphi在ScktComp單元中對WinSock進行了封裝,該單元提供了TAbstractSocket、TClientSocket、TClientWinSocket、TCustomSocket、TCustomWinSocket、TCustomServerSocket 、TServerClientThread、TServerWinSocket、 TServerClientWinSocket、、TServerSocket、TWinSocketStream等相關的類。
它的繼承關系為:
TAbstractSocket類是所有Socket組件的基類。
TCustomWinSocket類是所有WinSock對象的基類。
TClientSocket類用於管理客戶Socket連接。
TClientWinSocket類:客戶Socket組件(TClientSocket)使用它來為客戶應用管理WinSock API調用。
TServerSocket類為TCP/IP服務器管理服務器Socket連接。
TServerClientWinSocket類:服務器Socket組件(TServerSocket)使用它管理到一個客戶Socket的WinSock API調用。
TServerWinSocket類:服務器Socket組件(TServerSocket)使用它為TCP/IP監聽連接管理WinSock API調用。
TWinSocketStream類:在線程阻塞工作模式下提供到Socket連接的讀寫服務。
TServerClientThread類:用於為每一個到客戶Socket的獨立的連接創建一個線程。
要點:
TServerWinSocket類用於管理服務器端的Socket,TClientWinSocket類用於管理客戶端Socket。因為我們用控件的話,放置的控件是標准的Socket控件不能為Windows平台直接管理和使用,所以要用TServerWinSocket和TClientWinSocket來分別進行管理使其轉變為WinSock以便Windows平台更好的控制和使用。
服務器端Socket使用TServerClientWinSocket管理一個到客戶Socket的連接。TServerClientWinSocket與TServerWinSocket的區別是:當把一個TServerSocket組件放置到一個窗體或數據模塊上並進入監聽狀態后,就自動創建了TServerWinSocket,而只有當服務器Socket監聽到一個客戶Socket的連接請求時並接受了請求后,才創建一個TServerClientWinSocket。當客戶Socket的連接斷開時,自動刪除相應的TServerClientWinSocket。TServerClientWinSocket有一個ServerWinSokcet屬性,它返回處監聽狀態的服務器端Socket對象(TServerWinSocket)。
當你把服務端設置成stthreadblocking的阻塞方式的時候,OnRead根本不會被觸發,OnRead是在非阻塞的異步的時候才會被觸發.正確的方法(阻塞)是定義一個ClientThread的線程類,在ServerSocketGetThread的時候創建一個線程,處理客戶端請求..這樣就可以支持多個客戶端同時連接和收發封包. 至於客戶端,用非阻塞的方式就可以了.
疑問:
1、ClientSocket的ClientType為ctNonBlocking ctBlocking的區別?
2、ServerSocket的ServerType為stNonBlocking,stThreadBlocking的區別?
解答:
1、ctNonBlocking(非阻塞):它是異步進行讀寫操作,因此數據的傳輸不會阻塞應用程序中其他的代碼執行,使用非阻斷型連接時,當連接的另一端試圖讀寫信息時,會觸發OnReceive和OnSend事件來通知你的Socket,從Socket連接中讀取信息需要調用ReceiveBuf或ReceiveIn方法,寫信息需要調用SendBuf,SendStream或SendIn方法。當連接阻斷后,Socket必須主動在已建立的連接上讀寫信息,而不是被動地等待Socket連接來通知。如果想控制何時開始讀或寫,請使用阻斷型Socket。它是處理機制是通過消息處理。ctBlocking(阻斷):此為阻斷式。
2、對於服務器端Socket,將BlockMode屬性設置為bmBlocking或是bmThreadBlocking都可以建立阻斷型連接。當Socket正在等待完成某個請求的讀寫操作時,阻斷型連接會攔截住所有其他代碼的執行。當屬性是stThreadBlocking(線程阻塞模式)時,服務器端Socket組件總會為每一個客戶端連接生成一個新的執行線程。當屬性為bmBlocking時,程序被阻塞,直到一個新的連接建立。要在OngetThread中生成TServerClientThread來處理數據,這時ThreadCacheSize提供了一個線程池的功能。
二、多線程編程:
Delphi的VCL有一個缺陷,就是不支持多個線程同時訪問它。如果線程中要訪問VCL對象可以用Synchronize來實現,它用Method參數指定的方法去訪問VCL對象,實際上線程本人並不調用這個方法,而是通知主線程調用這個方法,主線程一次只能收到一個通知,這樣就避免了對VCL對象並發訪問。(注:任何繼承VCL中定義的類的對象就是VCL對象)
編寫線程代碼時,必須要考慮其他線程的影響,具體的說:一方面線程互斥,另一方面就是線程同步。互斥就是要解決多線程同時訪問一個全局變量,同步就是解決線程執行順序的問題(一個線程結束之后,自動喚醒等待它的運算結果的其它線程)。
線程互斥:要避免多個線程並發訪問全局變量時發生沖突,VCL中提供了三種解決方法:鎖定對象、設置臨界段、共享讀—獨占寫。
1、鎖定對象:一些對象本身就有Lock和UnLock方法,線程在操作這類對象時可以用這兩個方法,還有一些線程安全的對象,如TCanvas和TThreadlist,它們自身就有一種機制來保持線程安全,如TCanvas可以自動使用Lock和Unlock來鎖定和解鎖。
2、設置臨界段:如果沒Lock方法,可以考慮臨界段,它象一個門,同一時刻內只允許一個線程訪問。它是通過創建一個TCriticalSection的全局實例來實現的。它有兩個方法:Acquire(鎖定)和Release(開放)。注意,只有所有線程都是通過臨界段訪問與之相連的全局內存,這種方法才能起作用。
3、共享讀—獨占寫:設置臨界體段的方法有一個缺點,就是在同一時刻只能有一個線程訪問全局變量,而實際上我們不是總是改全局變量,很多情況只是讀,這並不對內存造成錯誤,這種情況我們可采用TMultiReadExclusiveWriteSynchronizer對象來實現多個線程同時讀全局變量,而只允許一個線程改。與設置臨界段相同的是,也要求所有訪問都采用這個對象,它有如下方法:BeginRead,EndRead,BeginWrite及EndWrite。
線程同步:如果一個線程必須等到其他線程的任務結束才能夠繼續,那么可以通知它暫時掛起。具有兩種方式:
1、 等待其他線程結束,調用WaitFor方法可以實現。
2、 等待一個作業完成,有時希望等待一個線程完成一些操作,而不是等待一個線程執行結束。如果是這樣,則需要一個事件對象TEvent,事件對象必須為全局對象,它對所有的線程都是可見的。當一個線程完成了其他線程所要求的任務,它就調用TEvent.SetEvent方法打開一個標志,其他線程可以檢查這個標志,從而得知需要的任務已經完成。如果要關掉這個標志,則應調用ResetEvent方法。
疑問:
線程局部變量與普通變量的區別?threadvar
答:線程的局部變量要想被線程中調用的函數來訪問,就要用threadvar來聲明這個變量,否則線程中調用的外部函數就不能訪問這個變量,這類變量只能被線程內的函數來訪問.