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!

¿Fue útil?

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top