Indy 10 IdTCPClient Dados Reading usando um segmento separado?
-
23-08-2019 - |
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!
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.)