http://blog.csdn.net/quicmous/article/details/4263115
最近使用了Delphi7提供的TcpCLient和TcpServer兩個VCL控件,發現這兩個控件使用非常簡單,不像網上不少帖子說的那樣難用、性能差。
TcpClient和TcpServer沒有采用事件驅動的模式,而是采取了同步方法調用的策略(網上有帖子稱之為阻塞模式),大大簡化了編寫Sockets通訊程序的過程。如果我們還停留在事件驅動的思維習慣上,就會發現很難使用這兩個控件。
Delphi7提供了一個名為NetChat的演示程序,該程序展示了TcpClient和TcpServer的初步用法。
一、 數據的發送與接收
//數據發送代碼: procedure TForm1.btnSendClick(Sender: TObject); var I: Integer; begin TcpClient1.RemoteHost := edtRemoteHost.Text; //服務器IP TcpClient1.RemotePort := edtRemotePort.Text; //服務器端口號 try if TcpClient1.Connect then for I := 0 to memSend.Lines.Count - 1 do TcpClient1.Sendln(memSend.Lines[I]); finally TcpClient1.Disconnect; end; end; //在OnAccept事件中接收數據(此處作了簡化): procedure TForm1.TcpServer1Accept(sender: TObject; ClientSocket: TCustomIpClient); var s: string; begin s := ClientSocket.Receiveln; while s <> '' do begin //此處對s做適當處理,代碼略。 s := ClientSocket.Receiveln; end; end;
TcpServer在接收到數據后,只產生一個OnAccept事件。在OnAccept時間響應函數中,我們循環調用ClientSocket參數的Receiveln方法接受數據。讓該方法返回空字符串時,意味着連接已經斷開。
需要注意的是:
1.Receiveln方法在客戶端沒有傳送數據時一直處於等待狀態。
2.演示程序用s <> ''判斷連接是否斷開,下面我們可以看到其它判斷方法。
二、 TcpServer的OnAccept事件中的參數ClientSocket
OnAccept事件響應函數帶一個名為ClientSocket的參數,該參數是一個TCustomIpClient類型的對象。利用該參數可以方便我們訪問客戶端信息(客戶端IP、端口等),並管理連接。
前面我們看到可以利用Receiveln返回空值的條件判斷連接是否結束。如果希望實現超時斷開連接功能的話,可以采用ClientSocket對象的WaitForData方法,等待數據。該方法允許指定等待時間,方法聲明如下:
function WaitForData(TimeOut: Integer = 0): boolean;
可以調用ClientSocket的Disconnect方法在退出前斷開連接。
三、 關於OnAccept多線程的問題
在同時收到多個客戶端信息時,OnAccept應該在內存存在多個線程實例。因此,在OnAccept的代碼,必須符合多線程設計原則。
Delphi的VCL庫和其它一些組件是面向單線程設計的。這表明對VCL控件等組件的調用,必須通過TThread類的Synchronize方法調用。在NetChat這個演示程序中,提供了相應的代碼供參考。更多資料請參考Delphi多線程編程的相關資料。
四、從服務器返回數據
如果需要從服務器返回數據的話,只需要在OnAccept中調用ClientSocket.Sendln方法即可。
客戶端需要做的就是在執行完TcpClient1.Sendln后,直接調用 TcpClient1.Receiveln即可。該方法會等待服務器傳回的信息。當然如果希望超時斷開的話,也可以調用WaitForData方法,判斷在給定時間內是否有數據返回。
四、 常見問題
問題:為什么TcpClient的OnReceive事件不響應服務器傳回的數據?
原因很簡單,OnReceive事件是由TcpClient的Receiveln等函數激發的,用於在Receiveln等函數接受到數據之前過濾數據用。
因此,如果你不調用TcpClient的Receiveln等函數,OnReceive事件就永遠不會被激發。