当前位置:  开发笔记 > 前端 > 正文

Indy 10 IdTCPClient使用单独的线程读取数据?

如何解决《Indy10IdTCPClient使用单独的线程读取数据?》经验,为你挑选了2个好方法。

问题:我正在寻找的是使用Indy 10中的IdTCPClient使用单独线程接收数据的最典型或最佳实践方法.

背景:下面的代码是为了清楚起见我要删除的实际数据处理部分的示例.线程的想法是接收所有数据(变量大小,标头声明消息长度的其余部分)然后解析它(这就是HandleData过程的作用)并根据命令触发事件处理程序.

TIdIOHandlerSocket由主应用程序传递给线程,主应用程序也在需要时将数据写入套接字.

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;

作为前缀,我使用了另一个StackOverflow问题来处理Indy的服务器组件:" Delphi 2009,Indy 10,TIdTCPServer.OnExecute,如何获取InputBuffer中的所有字节 "以获得我到目前为止的基础.

谢谢你的帮助!



1> jpfollenius..:

如果您想避免为每个客户端 - 服务器数据交换创建线程类所产生的开销,您可以创建一个运动线程类,如

http://delphidicas.blogspot.com/2008/08/anonymous-methods-when-should-they-be.html

几天前我遇到了同样的问题,我刚给我写了一个类TMotileThreading,它有静态函数,可以让我使用D2009的新匿名方法功能创建线程.看起来像这样:

type
  TExecuteFunc = reference to procedure;

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

第二个过程允许我像你的情况一样执行客户端 - 服务器通信,并在数据到达时执行一些操作.匿名方法的好处是你可以使用调用上下文的局部变量.所以沟通看起来像这样:

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

Execute和ExecuteThenCall方法只是创建一个工作线程,将FreeOnTerminate设置为true以简化内存管理并在工作线程的Execute和OnTerminate过程中执行提供的函数.

希望有所帮助.

编辑(根据要求完全实现类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;



2> Rob Kennedy..:

你走在正确的轨道上.Indy 旨在像这样使用.它使用阻塞套接字,因此在ReadBytes读取您要求的内容之前,调用不会返回.与非阻塞套接字相比,调用可以提前返回,因此您可以轮询或异步通知以确定何时填充请求.

Indy的设计期望套接字对象具有自己的线程(或光纤).Indy TIdAntifreeze为那些想要将套接字组件拖放到其表单和数据模块上并使用主GUI线程中的Indy组件的人们提供服务,但如果你能避免它,那通常不是一个好主意.

由于你的线程在没有FSocket被分配的情况下无法工作,我建议你只需在类的构造函数中接收该值.如果未分配,则在构造函数中断言.此外,创建一个非暂停的线程是一个错误,所以为什么甚至给出选项?(如果未创建线程暂停,则它将开始运行,检查是否FSocket已分配,并且因为创建线程尚未分配该字段而失败.)

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有