问题:我正在寻找的是使用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中的所有字节 "以获得我到目前为止的基础.
谢谢你的帮助!
如果您想避免为每个客户端 - 服务器数据交换创建线程类所产生的开销,您可以创建一个运动线程类,如
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;
你走在正确的轨道上.Indy 旨在像这样使用.它使用阻塞套接字,因此在ReadBytes
读取您要求的内容之前,调用不会返回.与非阻塞套接字相比,调用可以提前返回,因此您可以轮询或异步通知以确定何时填充请求.
Indy的设计期望套接字对象具有自己的线程(或光纤).Indy TIdAntifreeze
为那些想要将套接字组件拖放到其表单和数据模块上并使用主GUI线程中的Indy组件的人们提供服务,但如果你能避免它,那通常不是一个好主意.
由于你的线程在没有FSocket
被分配的情况下无法工作,我建议你只需在类的构造函数中接收该值.如果未分配,则在构造函数中断言.此外,创建一个非暂停的线程是一个错误,所以为什么甚至给出选项?(如果未创建线程暂停,则它将开始运行,检查是否FSocket
已分配,并且因为创建线程尚未分配该字段而失败.)