Indy 10 IdTCPClient Lectura de datos utilizando un hilo separado?
-
23-08-2019 - |
Pregunta
Pregunta: Lo que estoy buscando es la forma más típico o mejores prácticas para utilizar un hilo separado para recibir datos utilizando un IdTCPClient en Indy 10.
Antecedentes: El código siguiente es un ejemplo de lo que estoy tratando de hacer con las partes de procesamiento de datos reales eliminadas para mayor claridad. La idea de la rosca es para recibir todos los datos (tamaño variable con un encabezado que declara el resto de la longitud del mensaje) y luego a analizarlo (Eso es lo que hace el procedimiento HandleData) y desencadenar un controlador de eventos en función del comando.
El TIdIOHandlerSocket se pasa al hilo por la aplicación principal, que también escribe datos en el socket como y cuando se requiere.
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 un prefijo, he utilizado otra pregunta StackOverflow que se ocupa de los componentes del servidor de Indy: " Delphi 2009, Indy 10, TIdTCPServer.OnExecute, la forma de agarrar todos los bytes en el InputBuffer " para obtener la base de lo que tengo hasta ahora.
Gracias por cualquier ayuda!
Solución
Si se quiere evitar la sobrecarga impuesta por la creación de clases de hilo para todos y cada intercambio de datos cliente-servidor, se podría crear una clase de roscado móviles como se describe en
http: //delphidicas.blogspot. com / 2008/08 / anónimos-métodos-cuando-debe-que-be.html
Yo tenía el mismo problema hace unos días y yo sólo me escribió una TMotileThreading clase que tiene funciones estáticas que me permiten crear hilos usando la nueva característica del método anónimo D2009. Es como la siguiente:
type
TExecuteFunc = reference to procedure;
TMotileThreading = class
public
class procedure Execute (Func : TExecuteFunc);
class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc);
end;
El segundo procedimiento me permite establecer la comunicación cliente-servidor como en su caso y hacer algunas cosas cada vez que ha llegado la información. Lo bueno de los métodos anónimos es que se pueden utilizar las variables locales del contexto de llamada. Así que una comunicación es como la siguiente:
var
NewData : String;
begin
TMotileThreading.ExecuteThenCall (
procedure
begin
NewData := IdTCPClient.IOHandler.Readln;
end,
procedure
begin
GUIUpdate (NewData);
end);
end;
El Ejecutar y ExecuteThenCall método simplemente crear un subproceso de trabajo, establecer FreeOnTerminate true para simplificar la gestión de memoria y ejecutar las funciones previstas en los procedimientos de ejecución y OnTerminate. Subproceso de trabajo
Espero que ayude.
Editar (como se pide la plena aplicación de la clase 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;
Otros consejos
Usted está en el camino correcto. Indy es destinado para ser utilizado por el estilo. Se utiliza tomas de bloqueo , por lo que la llamada ReadBytes
no vuelve hasta que se lee lo que ha pedido. Esto contrasta con los zócalos no-bloqueo, en los que una llamada puede regresar temprano, así que o bien encuesta o ser notificado de forma asíncrona para determinar cuando una solicitud se ha llenado.
Indy está diseñado con la expectativa de que los objetos de socket tienen sus propios hilos (o fibras). Indy viene con TIdAntifreeze
para las personas que quieren arrastrar y soltar componentes de socket en sus formas y módulos de datos y utilizar los componentes Indy desde el hilo principal interfaz gráfica de usuario, pero eso no es generalmente una buena idea si se puede evitar.
Dado que el hilo no puede funcionar sin FSocket
de ser asignado, yo le aconsejo que simplemente recibe ese valor en el constructor de la clase. Afirmarse en el constructor si no está asignado. Por otra parte, se trata de un error para crear no suspende el hilo, ¿por qué siquiera dan la opción? (Si el hilo no se crea suspendido, entonces se pondrá en marcha, comprobar si se asigna FSocket
, y fallar porque el hilo de la creación no ha conseguido asignar ese campo todavía.)