Indy 10 IdTCPClient Lettura dei dati utilizzando un thread separato?
-
23-08-2019 - |
Domanda
Domanda: Quello che sto cercando è il modo in cui più tipico o migliori pratiche di utilizzare un thread separato per ricevere dati utilizzando un IdTCPClient in Indy 10.
Sfondo: Il codice che segue è un esempio di quello che sto cercando di fare con le parti di elaborazione dati effettivi rimosse per chiarezza. L'idea del Filo è quello di ricevere tutti i dati (dimensione variabile con un colpo di testa che dichiara il resto della lunghezza del messaggio) e poi di analizzarlo (Questo è ciò che la procedura HandleData fa) ed attivare un gestore eventi a seconda del comando.
Il TIdIOHandlerSocket viene passato al filo mediante l'applicazione principale che scrive dati al socket come e quando è necessario.
TScktReceiveThread = class(TThread)
private
{ Private declarations }
procedure HandleData;
protected
procedure Execute; override;
public
FSocket: TIdIOHandlerSocket;
constructor Create(CreateSuspended: boolean);
end;
procedure TScktReceiveThread.Execute;
var
FixedHeader: TBytes;
begin
Assert(FSocket <> nil, 'You must assign the connected socket to the receiving thread');
SetLength(FixedHeader, 2);
while not Terminated do
begin
if not FSocket.Connected then
Suspend
else
begin
FSocket.CheckForDataOnSource(10);
if not FSocket.InputBufferIsEmpty then
begin
FSocket.ReadBytes(FixedHeader, SizeOf(FixedHeader), false);
// Removed the rest of the reading and parsing code for clarity
Synchronize(HandleData);
end;
end;
end;
end;
come prefisso, ho usato un'altra domanda StackOverflow che si occupa dei componenti server di Indy: " Delphi 2009, Indy 10, TIdTCPServer.OnExecute, come per afferrare tutti i byte nel INPUTBUFFER " per ottenere la base di quello che ho finora.
Grazie per qualsiasi aiuto!
Soluzione
Se si vuole evitare il sovraccarico imposto con la creazione di classi di thread per ogni scambio di dati client-server, è possibile creare una classe filettatura motile come descritto in
http: //delphidicas.blogspot. COM / 2008/08 / anonymous-metodi-quando-deve-hanno-be.html
Ho avuto lo stesso problema qualche giorno fa e mi ho appena scritto una classe TMotileThreading che ha funzioni statiche che mi permette di creare discussioni utilizzando la nuova funzione metodo anonimo del D2009. Sembra qualcosa di simile:
type
TExecuteFunc = reference to procedure;
TMotileThreading = class
public
class procedure Execute (Func : TExecuteFunc);
class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc);
end;
La seconda procedura mi permette di stabilire una comunicazione client-server, come nel tuo caso e fare alcune cose ogni volta che i dati sono arrivato. La cosa bella di metodi anonimi è che è possibile utilizzare le variabili locali del contesto di chiamata. Quindi una comunicazione simile a questa:
var
NewData : String;
begin
TMotileThreading.ExecuteThenCall (
procedure
begin
NewData := IdTCPClient.IOHandler.Readln;
end,
procedure
begin
GUIUpdate (NewData);
end);
end;
La Eseguire e ExecuteThenCall metodo semplicemente creare un thread di lavoro, impostare FreeOnTerminate true per semplificare la gestione della memoria ed eseguire le funzioni previste nella Eseguire e di procedure OnTerminate. Thread di lavoro
La speranza che aiuta.
Modifica (come richiesto la piena attuazione della classe TMotileThreading)
type
TExecuteFunc = reference to procedure;
TMotileThreading = class
protected
constructor Create;
public
class procedure Execute (Func : TExecuteFunc);
class procedure ExecuteAndCall (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean = False);
end;
TMotile = class (TThread)
private
ExecFunc : TExecuteFunc;
TerminateHandler : TExecuteFunc;
SyncTerminateHandler : Boolean;
public
constructor Create (Func : TExecuteFunc); overload;
constructor Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean); overload;
procedure OnTerminateHandler (Sender : TObject);
procedure Execute; override;
end;
implementation
constructor TMotileThreading.Create;
begin
Assert (False, 'Class TMotileThreading shouldn''t be used as an instance');
end;
class procedure TMotileThreading.Execute (Func : TExecuteFunc);
begin
TMotile.Create (Func);
end;
class procedure TMotileThreading.ExecuteAndCall (Func : TExecuteFunc;
OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean = False);
begin
TMotile.Create (Func, OnTerminateFunc, SyncTerminateFunc);
end;
constructor TMotile.Create (Func : TExecuteFunc);
begin
inherited Create (True);
ExecFunc := Func;
TerminateHandler := nil;
FreeOnTerminate := True;
Resume;
end;
constructor TMotile.Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean);
begin
inherited Create (True);
ExecFunc := Func;
TerminateHandler := OnTerminateFunc;
SyncTerminateHandler := SyncTerminateFunc;
OnTerminate := OnTerminateHandler;
FreeOnTerminate := True;
Resume;
end;
procedure TMotile.Execute;
begin
ExecFunc;
end;
procedure TMotile.OnTerminateHandler (Sender : TObject);
begin
if Assigned (TerminateHandler) then
if SyncTerminateHandler then
Synchronize (procedure
begin
TerminateHandler;
end)
else
TerminateHandler;
end;
Altri suggerimenti
Sei sulla strada giusta. Indy è destinato da utilizzare in quel modo. Esso utilizza blocco prese , quindi la chiamata ReadBytes
non ritorna fino a quando non ha letto quello che hai chiesto. Contrasto che, con prese non bloccanti, in cui una chiamata può tornare presto, in modo che sia sondaggio o ricevere la notifica in modo asincrono per determinare quando una richiesta è stata riempita.
Indy è stato progettato con l'aspettativa che gli oggetti socket hanno i loro filetti (o fibre). Indy viene fornito con TIdAntifreeze
per le persone che vogliono trascinare i componenti presa sulle loro forme e moduli di dati e utilizzare i componenti Indy dal thread GUI principale, ma non è generalmente una buona idea se si può evitare.
Dal momento che il filo non può funzionare senza FSocket
essere assegnato, vi consiglio di ricevere semplicemente che il valore nella costruzione della classe. Assert nel costruttore, se non è stato assegnato. Inoltre, si tratta di un Errore per creare il tuo thread non ha sospeso, quindi perché anche dare la possibilità? (Se il filo non viene creato sospesa, allora inizierà esecuzione, controllare se FSocket
viene assegnato e sicuro perché il thread creando non ha ottenuto assegnare ancora quel campo.)