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!

È stato utile?

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.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top