Indy TCPServer не получает все пакеты от TCPClient при слишком частой отправке пакетов - PullRequest
4 голосов
/ 07 февраля 2012

У меня проблема с Indy TCP-соединением.Я использую Turbo Delphi 2006 с Indy 10. Я хочу отправить несколько пакетов TCP из idTCPClient в idTCPServer.

Это прекрасно работает, когда я хочу отправить только один пакет или вставляю команду sleep (100)два вызова функции.Но если я вызываю эту функцию слишком часто, она не вызывает каждый раз onExecute сервера.

Мой код для отправки:

procedure SendData(var data: TIdBytes) ;
begin
  FormMain.IdTCPClient.Connect ;
  FormMain.IdTCPClient.Socket.Write(data);
  FormMain.IdTCPClient.Disconnect ;
end ;

Я вызываю эту функцию несколько раз (5-10раз в секунду) и хочу обработать все эти пакеты в моем серверном приложении:

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
  data: TIdBytes ;
begin
  AContext.Connection.IOHandler.ReadBytes(data, 4, false)
  // processing data
end

Спасибо за ваши ответы заранее!

1 Ответ

4 голосов
/ 07 февраля 2012

Каждый раз, когда вы вызываете Connect(), вы создаете новое соединение, и TIdTCPServer запускает новый поток для обработки этого соединения (если вы не включили пул потоков, то есть). Это то, что вы действительно хотите? Было бы более эффективно, если бы клиент оставил соединение открытым на некоторое время и максимально использовал существующее соединение. Разъединяйте соединение только тогда, когда оно вам действительно не нужно, например, когда оно простаивает некоторое время. Установление нового соединения - дорогостоящая операция на обоих концах, поэтому вы должны максимально сократить эти издержки.

На стороне клиента, когда вы вызываете Write(data), он отправит весь TIdBytes, но вы не отправляете длину этого TIdBytes на сервер, поэтому он знает, сколько байтов ожидать. TIdIOHandler.Write(TIdBytes) не делает это для вас, вы должны сделать это вручную.

На стороне сервера вы говорите ReadBytes() читать только 4 байта за раз. После каждого блока из 4 байтов вы выходите из обработчика события OnExecute и ожидаете его повторного вызова для чтения следующего блока из 4 байтов. Если длина клиентского источника TIdBytes не кратна 4, ReadBytes() вызовет исключение (в результате чего сервер отключит соединение), когда он попытается прочитать последний блок клиента, который составляет менее 4 байтов, поэтому код вашего сервера не получит этот блок.

Попробуйте вместо этого:

procedure SendData(var data: TIdBytes) ; 
begin 
  FormMain.IdTCPClient.Connect; 
  try
    FormMain.IdTCPClient.IOHandler.Write(Longint(Length(data))); 
    FormMain.IdTCPClient.IOHandler.Write(data); 
  finally
    FormMain.IdTCPClient.Disconnect; 
  end;
end; 

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
var 
  data: TIdBytes; 
begin 
  with AContext.Connection.IOHandler do
    ReadBytes(data, ReadLongint, false);
  // process data 
end;

С учетом вышесказанного, если изменение кода клиента для отправки длины TIdBytes по каким-либо причинам невозможно, используйте вместо этого код сервера:

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
var 
  LBytes: Integer;
  data: TIdBytes; 
begin 
  // read until disconnected. returns -1 on timeout, 0 on disconnect
  repeat until AContext.Connection.IOHandler.ReadFromSource(False, 250, False) = 0;
  AContext.Connection.IOHandler.InputBuffer.ExtractToBytes(data);
  // process data 
end;

Или:

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
var 
  strm: TMemoryStream;
  data: TIdBytes;
begin 
  strm := TMemoryStream.Create;
  try
    // read until disconnected
    AContext.Connection.IOHandler.ReadStream(strm, -1, True);
    strm.Position := 0;
    ReadTIdBytesFromStream(strm, data, strm.Size);
  finally
    strm.Free;
  end;
  // process data 
end;

Или:

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
var 
  strm: TMemoryStream;
begin 
  strm := TMemoryStream.Create;
  try
    // read until disconnected
    AContext.Connection.IOHandler.ReadStream(strm, -1, True);
    // process strm.Memory up to strm.Size bytes
  finally
    strm.Free;
  end;
end;
...