2011-12-07 16 views
17

Come può del TIdTCPClient e TIdTCPServer essere utilizzato nel seguente scenario Indy:Indy TCP Client/Server con il client che funge da server

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

Il cliente avvia la connessione, ma agisce come un "server" (aspettando i comandi e eseguendoli).

L'approccio OnExecute di TIdTCPServer non funziona bene in questo caso (almeno non riesco a farlo funzionare correttamente). Come potrei farlo?

Spero che la domanda sia abbastanza chiara.

+2

Forse è possibile simulare questo comportamento con i metodi 'IdTCPClient1.IOHandler.ReadLnWait' o' IdTCPClient1.IOHandler.WaitFor'. Altrimenti, usare 'TIdCmdTCPCLient' potrebbe risolvere il tuo problema. – LightBulb

+0

Quale versione di Indy? –

+0

Attualmente, sto usando Indy 10 in Delphi 2010. – LightBulb

risposta

17

Non c'è nulla che ti impedisce di farlo con il componente TIdTCPServer di Indy.

Un TIdTCPServer imposta solo la connessione. Dovrai implementare il resto. Quindi la sequenza di invio e ricezione effettivi può essere qualsiasi cosa tu voglia.

inserire questo codice nell'evento OnExecute del componente TIdTCPServer:

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; 

Ecco come è possibile impostare la TIdTCPServer davvero semplice:

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

Questo indica al server per l'ascolto solo sull'indirizzo di loopback, alla porta 8080. Ciò impedisce a chiunque al di fuori del computer di connettersi ad esso.

Poi, per collegare il cliente, si può andare a un prompt dei comandi di Windows e digitare quanto segue:

telnet 127.0.0.1 8080 

ecco l'output:

Qual è il tuo nome?

Marcus

Ciao, Marco.

Ti piacerebbe giocare?

Connessione all'host perso.

Non hai telnet? Ecco come fare per install telnet client on Vista and 7.

o con un client TIdTCP, si può fare questo:

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; 

Una cosa importante da notare qui è che le dichiarazioni readLn aspettare fino a quando non ci sono dati. Questa è la magia dietro a tutto.

+7

La "magia" dovrebbe essere inserita nella sua stessa thread in modo che l'applicazione possa continuare mentre il server non ha nulla da dire – mjn

5

Quando il client si collega al server, il server ha un evento OnConnect con un parametro AContext: TIdContext.

Una proprietà di questo è AContext.Connection, che è possibile memorizzare al di fuori di tale evento (ad esempio, in una matrice). Se lo si associa all'IP o, meglio ancora, a un ID sessione generato, quindi si fa riferimento a tale Connessione in base a tali criteri, è possibile fare in modo che il server invii comandi o messaggi ad hoc al client.

Spero che questo aiuti!

+0

Chiede se un cliente può ascoltare i comandi dal server. – LightBulb

+2

Sì, e questa soluzione lo consente! Il server deve solo memorizzare la connessione di ciascun client in modo che possa passare messaggi o comandi direttamente a uno o più client senza essere limitato a rispondere prima ai comandi del client! – LaKraven

+0

E come farà il Cliente a sapere di aver ricevuto un comando se non ci sono thread di ascolto sul lato client? – LightBulb

-3

Con Indy questo non è possibile in base alla progettazione:
Indy supporta solo la comunicazione avviata dal client, il che significa che il server può solo inviare una risposta alle richieste da parte del client.
Il modo più semplice (ma non il più intelligente) per ottenere quello che vuoi è utilizzare un processo di pull. Controllato da un timer i client chiedono al server se c'è un nuovo comando. Ovviamente questo causerà un sacco di sovraccarico del traffico e, a seconda del tuo pull-intervall, c'è un ritardo.
In alternativa è possibile utilizzare un'altra libreria come ICS (http://www.overbyte.be/eng/products/ics.html)

+1

Questo è davvero un difetto di design di Indy? Ho sempre pensato che si trattasse di una limitazione TCP ... –

+0

Oh, beh in tal caso, suppongo che siamo come Tom Cruise in "Mission impossible", perché senza alcun tentativo dai nostri clienti, il nostro server invia messaggi ai nostri clienti, ad esempio quando sta chiudendo, o per dire a tutti i client connessi di cambiamenti in determinati dati ... –

+3

IIUC La comunicazione TCP/IP è: 1. Il client si connette al server (la parte che avvia la connessione è il client per definizione), 2.Entrambe le parti possono inviare dati in qualsiasi momento (perché un socket TCP/IP è bidirezionale) – mjn

4

normalmente il client e lato server hanno un filo che sta leggendo telegrammi in arrivo, e l'invio di telegrammi in sospeso ... ma questo tipo di protocolli (Invia/Ricevi, quando e cosa) dipendono dall'applicazione.

3

Un ottimo punto di partenza su come il client può essere implementato utilizzando un thread, ascoltando i messaggi dal server, è il componente client Indy Telnet (TIdTelnet nella cartella Protocolli).

il client telnet Indy si collega al server telnet e utilizza solo una presa di scrivere e leggere i dati. La lettura avviene in un thread di ascolto.

Questo progetto può essere facilmente adattato per creare software di messaggistica distribuito come chat, ecc. E mostra anche quanto sia facile disaccoppiare il protocollo dal livello di rete utilizzando socket di blocco.

+0

Ho implementato un protocollo come questo prima e basato sul codice client telnet. – MikeT

6

Se i vostri comandi sono di natura testuale, date un'occhiata al componente TIdCmdTCPClient, è specificamente progettato per situazioni in cui il server invia comandi invece del client. Il server può utilizzare TIdContext.Connection.IOHandler.WriteLn() o TIdContext.Connection.IOHandler.SendCmd() per inviare i comandi.

Problemi correlati