Indy 10 IdTCPClient Считывает данные с помощью отдельного потока?

StackOverflow https://stackoverflow.com/questions/554142

Вопрос

Вопрос: То, что я ищу, это наиболее типичная или наилучшая практика способ использования отдельного потока для получения данных с использованием 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;

В качестве префикса я использовал другой вопрос StackOverflow, который касается серверных компонентов Indy:"Delphi 2009, Indy 10, TIdTCPServer.OnExecute, как захватить все байты во входном буфере" чтобы получить основу для того, что у меня есть на данный момент.

Спасибо за любую помощь!

Это было полезно?

Решение

Если вы хотите избежать накладных расходов, связанных с созданием классов потоков для каждого обмена данными клиент-сервер, вы могли бы создать класс подвижных потоков, как описано в

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;

Другие советы

Вы на правильном пути.Инди - это предназначенный чтобы тебя вот так использовали.Он использует блокирующие розетки, так что ReadBytes вызов не перезвонит до тех пор, пока не будет прочитано то, что вы запросили.Сравните это с неблокирующими сокетами, где вызов может вернуться раньше, поэтому вы либо проводите опрос, либо получаете уведомление асинхронно, чтобы определить, когда запрос был выполнен.

Indy разработан с расчетом на то, что объекты сокета будут иметь свои собственные потоки (или волокна).Инди поставляется с TIdAntifreeze для людей, которые хотят перетаскивать компоненты сокетов в свои формы и модули данных и использовать компоненты Indy из основного потока GUI, но обычно это не очень хорошая идея, если вы можете этого избежать.

Поскольку ваш поток не может работать без FSocket будучи назначенным, я советую вам просто получить это значение в конструкторе класса.Подтвердите в конструкторе, если он не назначен.Кроме того, это ошибка чтобы создать свой поток без приостановки, так зачем вообще давать такую возможность?(Если созданный поток не приостановлен, то он начнет выполняться, проверьте, является ли FSocket назначается и завершается неудачей, потому что создающий поток еще не получил возможность назначить это поле.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top