2011-12-07 83 views
17

哪有Indy的TIdTCPClientTIdTCPServer在以下情況下使用:印TCP客戶機/服務器

Client ---------- initate connection -----------> Server 
... 
Client <---------------command------------------- Server 
Client ----------------response-----------------> Server 
... 
Client <---------------command------------------- Server 
Client ----------------response-----------------> Server 

客戶發起連接,但作爲一個「服務器」 (等待命令並執行它們)。

OnExecute方法TIdTCPServer在這種情況下不起作用(至少我沒有得到它的工作)。我怎麼能這樣做?

我希望問題很清楚。

+2

也許你可以用'IdTCPClient1.IOHandler.ReadLnWait'或'IdTCPClient1.IOHandler.WaitFor'方法來模擬這種行爲。否則,使用'TIdCmdTCPCLient'可以解決你的問題。 – LightBulb

+0

哪個版本的Indy? –

+0

目前,我在德爾福2010年使用Indy 10。 – LightBulb

回答

17

沒有什麼能夠阻止你使用Indy的TIdTCPServer組件做到這一點。

TIdTCPServer只設置連接。你需要實施其餘的。所以實際發送和接收的順序可以是任何你想要的。

將這個代碼在你TIdTCPServer組件的OnExecute事件:

var 
    sName: String; 
begin 
    // Send command to client immediately after connection 
    AContext.Connection.Socket.WriteLn('What is your name?'); 
    // Receive response from client 
    sName := AContext.Connection.Socket.ReadLn; 
    // Send a response to the client 
    AContext.Connection.Socket.WriteLn('Hello, ' + sName + '.'); 
    AContext.Connection.Socket.WriteLn('Would you like to play a game?'); 
    // We're done with our session 
    AContext.Connection.Disconnect; 
end; 

這裏是如何您可設置TIdTCPServer真正簡單:

IdTCPServer1.Bindings.Clear; 
IdTCPServer1.Bindings.Add.SetBinding('127.0.0.1', 8080); 
IdTCPServer1.Active := True; 

這告訴服務器只在回送地址聽,在端口8080.這可以防止計算機外部的任何人連接到它。

然後,連接你的客戶,你可以去到Windows命令提示符,然後鍵入以下內容:

telnet 127.0.0.1 8080 

下面是輸出:

你叫什麼名字?

馬庫斯

你好,馬庫斯。

你想玩遊戲嗎?

與主機連接丟失。

沒有telnet?以下是install telnet client on Vista and 7的操作方法。

或者用TIdTCP客戶端,你可以這樣做:

var 
    sPrompt: String; 
    sResponse: String; 
begin 
    // Set port to connect to 
    IdTCPClient1.Port := 8080; 
    // Set host to connect to 
    IdTCPClient1.Host := '127.0.0.1'; 
    // Now actually connect 
    IdTCPClient1.Connect; 
    // Read the prompt text from the server 
    sPrompt := IdTCPClient1.Socket.ReadLn; 
    // Show it to the user and ask the user to respond 
    sResponse := InputBox('Prompt', sPrompt, ''); 
    // Send user's response back to server 
    IdTCPClient1.Socket.WriteLn(sResponse); 
    // Show the user the server's final message 
    ShowMessage(IdTCPClient1.Socket.AllData); 
end; 

,這裏要注意的重要一點是,ReadLn陳述等到有數據。這是所有背後的魔力。

+7

'魔術'應該放在它自己的線程中,以便應用程序可以繼續,而服務器無話可說 – mjn

5

當客戶端連接到服務器時,服務器有一個帶有AContext: TIdContext參數的OnConnect事件。

此屬性爲AContext.Connection,您可以在該事件之外存儲(例如,在數組中)。如果將它與IP配對,或者生成的會話ID更好,然後通過該條件引用Connection,則可以讓服務器向客戶端發送特定命令或消息。

希望這會有所幫助!

+0

他問客戶端是否可以偵聽來自服務器的命令。 – LightBulb

+2

是的,這個解決方案允許!服務器只需要存儲每個客戶端的連接,以便它可以將消息或命令直接傳遞給一個或多個客戶端,而不必侷限於首先響應客戶端命令! – LaKraven

+0

如果客戶端沒有監聽線程,客戶如何知道它收到了一條命令? – LightBulb

-3

對於Indy,這是不可能的設計:
Indy只支持客戶端發起的通信,這意味着服務器只能發送對客戶端請求的響應。
最簡單的方法(但不是最聰明的)得到你想要的就是使用拉過程。由定時器控制客戶機詢問服務器是否有新命令。當然,這會造成很大的流量開銷,並且取決於你的拉間隔有一個延遲。
另外,您可以使用像ICS另一個庫(http://www.overbyte.be/eng/products/ics.html

+1

這是Indy的設計缺陷嗎?我一直認爲這是一個TCP限制... –

+0

哦,在那種情況下,我想我們就像湯姆克魯斯在「不可能的任務」,因爲沒有從我們的客戶拉,我們的服務器發送消息給我們的客戶,例如當它正在關閉,或者告訴所有連接的客戶端某些數據發生變化...... –

+3

IIUC TCP/IP通信是:1.客戶端連接到服務器(根據定義,發起連接的一方是客戶端),2。雙方可以隨時發送數據(因爲TCP/IP套接字是雙向的) – mjn

4

通常在客戶端和服務器端有一個線程正在讀來電,併發送未決電報......但這種協議(發送/接收,何時何地)取決於應用程序。

3

Indy Telnet客戶端組件(協議文件夾中的TIdTelnet)是一個很好的起點,客戶端如何使用線程實現使用線程來監聽來自服務器的消息。

印地遠程登錄客戶端連接到Telnet服務器,並使用只有一個插座讀取數據。讀取發生在一個監聽器線程中。

這個設計可以很容易地適用於構建聊天等分佈式消息傳遞軟件,並且還顯示了使用阻塞套接字可以將協議從網絡層分離出來是多麼容易。

+0

我已經實現了一個像這樣的協議,併成功地將其基於telnet客戶端代碼。 – MikeT

6

如果您的命令本質上是文本的,那麼請查看TIdCmdTCPClient組件,它專門設計用於服務器發送命令而不是客戶端的情況。服務器可以使用TIdContext.Connection.IOHandler.WriteLn()TIdContext.Connection.IOHandler.SendCmd()來發送命令。