Question

Question: Qu'est-ce que je cherche est le le plus typique ou les meilleures pratiques façon d'utiliser un thread séparé pour recevoir des données au moyen d'un IdTCPClient dans Indy 10.

Contexte: Le code ci-dessous est un échantillon de ce que je suis en train de faire avec les parties de traitement de données réelles supprimées pour plus de clarté. L'idée du fil est de recevoir toutes les données (taille variable avec un en-tête déclarant le reste de la longueur du message) et ensuite l'analyser (C'est ce que la procédure HandleData fait) et déclencher un gestionnaire d'événements en fonction de la commande.

Le TIdIOHandlerSocket est passé sur le fil par l'application principale qui écrit aussi des données sur la prise et que lorsque cela est nécessaire.

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;

En tant que préfixe, j'ai utilisé une autre question StackOverflow qui traite des composants du serveur d'Indy: " Delphi 2009, Indy 10, TIdTCPServer.OnExecute, comment saisir tous les octets dans le InputBuffer " pour obtenir la base de ce que j'ai jusqu'à présent.

Merci pour toute aide!

Était-ce utile?

La solution

Si vous voulez éviter les frais généraux imposés par la création de classes de fil pour chaque échange de données client-serveur, vous pouvez créer une classe de filetage motiles comme décrit dans

http: //delphidicas.blogspot. com / 2008/08 / anonymes-méthodes quand devraient-ils-be.html

J'ai eu le même problème il y a quelques jours et je me écrit une classe TMotileThreading qui a des fonctions statiques qui me permettent de créer des threads en utilisant la nouvelle fonctionnalité de méthode anonyme de D2009. On dirait quelque chose comme ceci:

type
  TExecuteFunc = reference to procedure;

  TMotileThreading = class
  public
    class procedure Execute (Func : TExecuteFunc);
    class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc);
  end;

La deuxième procédure me permet d'effectuer une communication client-serveur comme dans votre cas et faire des choses à chaque fois que les données sont arrivées. La bonne chose à propos des méthodes anonymes est que vous pouvez utiliser les variables locales du contexte d'appel. Ainsi, une communication ressemble à quelque chose comme ceci:

var
  NewData  : String;
begin
  TMotileThreading.ExecuteThenCall (
    procedure
    begin
      NewData := IdTCPClient.IOHandler.Readln;
    end,
    procedure
    begin
      GUIUpdate (NewData);
    end);
 end;

L'exécution et la méthode ExecuteThenCall simplement créer un thread de travail, mis FreeOnTerminate true pour simplifier la gestion de la mémoire et exécuter les fonctions prévues dans le thread de travail de exécution et les procédures OnTerminate.

L'espoir qui aide.

EDIT (comme l'a demandé la mise en œuvre complète de la 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;

Autres conseils

Vous êtes sur la bonne voie. Indy est destiné à utiliser comme ça. Il utilise prises blocage , de sorte que l'appel ReadBytes ne retourne pas jusqu'à ce qu'il soit lu ce que vous avez demandé. Voilà qui contraste avec les prises non-blocage, où un appel peut revenir au début,

afin que vous soit sondage ou être averti de manière asynchrone pour déterminer si une demande a été remplie.

Indy est conçu dans l'espoir que les objets socket ont leurs propres fils (ou fibres). Indy est livré avec TIdAntifreeze pour les gens qui veulent faire glisser et déposer les composants de prise sur leurs formes et modules de données et utiliser les composants Indy du fil de l'interface graphique principale, mais qui est généralement pas une bonne idée si vous pouvez l'éviter.

Étant donné que votre fils ne peut pas fonctionner sans FSocket être affecté, je vous conseille de recevoir simplement cette valeur dans le constructeur de la classe. Affirmez dans le constructeur si elle est non affecté. De plus, il est un erreur pour créer votre fil non suspendu, alors pourquoi même donner l'option? (Si le fil ne se crée pas suspendu, il commencera à fonctionner, vérifier si FSocket est affecté, et échouent parce que le fil n'a pas obtenu la création d'attribuer ce champ encore.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top