Indy 10 IdTCPClient Чтение данных с использованием отдельного потока?
Вопрос: То, что я ищу, - это наиболее типичный или лучший способ использования отдельного потока для получения данных с использованием IdTCPClient в Indy 10.
Справочная информация: приведенный ниже код является примером того, что я пытаюсь сделать с фактическими частями обработки данных, удаленными для ясности. Идея потока состоит в том, чтобы получить все данные (переменный размер с заголовком, определяющим остальную часть длины сообщения), а затем проанализировать их (это то, что делает процедура 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;
В качестве префикса я использовал другой вопрос Stackru, который касается серверных компонентов Indy: " Delphi 2009, Indy 10, TIdTCPServer.OnExecute, как получить все байты в InputBuffer", чтобы получить основу того, что у меня до сих пор,
Спасибо за любую помощь!
2 ответа
Если вы хотите избежать накладных расходов, связанных с созданием классов потоков для каждого и каждого обмена данными между клиентом и сервером, вы можете создать класс подвижных потоков, как описано в
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
для людей, которые хотят перетаскивать компоненты сокетов на свои формы и модули данных и использовать компоненты Indy из основного потока GUI, но это, как правило, не очень хорошая идея, если вы можете избежать этого.
Так как ваш поток не может работать без FSocket
будучи назначенным, я советую вам просто получить это значение в конструкторе класса. Утвердите в конструкторе, если он не назначен. Более того, ошибочно создавать ваш поток без приостановки, так зачем вообще давать эту опцию? (Если поток не создан приостановленным, то он начнет работать, проверьте, FSocket
назначается и завершается неудачно, потому что потоку создания еще не удалось назначить это поле.)