Pergunta

Pergunta: O que eu estou procurando é a mais típico ou melhores práticas maneira de usar um segmento separado para receber dados usando um IdTCPClient na Indy 10.

Fundo: O código abaixo é uma amostra do que eu estou tentando fazer com os dados reais de processamento de peças removidas para maior clareza. A ideia da linha é para receber todos os dados (tamanho variável com um cabeçalho declarando o resto do comprimento da mensagem) e, em seguida, analisá-lo (isso é o que o procedimento HandleData faz) e acionar um manipulador de eventos, dependendo do comando.

O TIdIOHandlerSocket é passado para o segmento pelo aplicativo principal, que também grava dados no soquete como e quando é necessário.

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;

Como um prefixo, eu usei uma outra questão StackOverflow que lida com os componentes de servidor de Indy: " Delphi 2009, Indy 10, TIdTCPServer.OnExecute, como agarrar todos os bytes no InputBuffer " para obter a base o que eu tenho até agora.

Obrigado por qualquer ajuda!

Foi útil?

Solução

Se você quiser evitar a sobrecarga imposta pela criação de classes de rosca para cada troca de dados cliente-servidor, você pode criar uma classe móveis de threading, conforme descrito no

http: //delphidicas.blogspot. COM / 2008/08 / anónimos-methods-quando-deve-se-be.html

Eu tive o mesmo problema há alguns dias e eu só me escreveu uma TMotileThreading classe que tem funções estáticas que permitem me criar tópicos usando o novo recurso método anônimo de D2009. É algo como isto:

type
  TExecuteFunc = reference to procedure;

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

O segundo procedimento permite-me para executar uma comunicação cliente-servidor, como no seu caso e fazer alguma coisa sempre que os dados já chegou. A coisa agradável sobre métodos anônimos é que você pode usar as variáveis ??locais do contexto de chamada. Assim, uma comunicação é algo como isto:

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

O Execute e ExecuteThenCall método simplesmente criar um segmento de trabalho, definir FreeOnTerminate como true para simplificar o gerenciamento de memória e executar as funções fornecidas no do segmento de trabalho Executar e procedimentos OnTerminate.

Espero que ajude.

Editar (tal como solicitado a plena implementação da 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;

Outras dicas

Você está no caminho certo. Indy é destinado para ser usada assim. Ele usa o bloqueio soquetes , para que a chamada ReadBytes não retornar até que leitura é o que você pediu. Contraste isso com soquetes sem bloqueio, em que uma chamada pode retornar mais cedo, assim que você quer enquete ou ser notificado de forma assíncrona para determinar quando um pedido tenha sido preenchido.

Indy é projetado com a expectativa de que os objetos de soquete têm os seus próprios tópicos (ou fibras). Indy vem com TIdAntifreeze para as pessoas que querem componentes de arrastar e soquete de queda sobre suas formas e módulos de dados e usar os componentes de Indy do thread principal GUI, mas isso não é geralmente uma boa idéia se você pode evitá-lo.

Desde a sua discussão não pode funcionar sem FSocket sendo atribuído, eu aconselho que você simplesmente receber esse valor no construtor da classe. Assert no construtor se não for atribuído. Além disso, é um erro para criar seu segmento não-suspenso, então por que dar a opção? (Se a linha não é criado suspenso, então ele vai começar a correr, verificar se FSocket é atribuído, e não conseguem porque o segmento criando não tenha chegado a atribuir esse campo ainda.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top