Вопрос

Я знаю, я задаю много вопросов... но как новый Delphi-разработчик, я постоянно задаюсь этими вопросами :)

Здесь речь идет о TCP-связи с использованием Indy 10.Чтобы сделать связь эффективной, я кодирую запрос операции клиента как один байт (в большинстве сценариев, конечно, за ним следуют другие байты данных, но в данном случае только один байт).Проблема в том, что

var Bytes : TBytes;
...
SetLength (Bytes, 1);
Bytes [0] := OpCode;
FConnection.IOHandler.Write (Bytes, 1);
ErrorCode := Connection.IOHandler.ReadByte;

не отправляет этот байт немедленно (по крайней мере, обработчик выполнения сервера не вызывается).Если я, например, изменю «1» на «9», все будет работать нормально.Я предположил, что Indy буферизует исходящие байты, и попытался отключить буферизацию записи с помощью

FConnection.IOHandler.WriteBufferClose;

но это не помогло.Как я могу отправить один байт и убедиться, что он отправлен немедленно?И - я добавляю еще один небольшой вопрос - как лучше всего отправить целое число с помощью indy?К сожалению, я не могу найти такую ​​функцию, как WriteInteger, в IOHandler TIdTCPServer... и

WriteLn (IntToStr (SomeIntVal))

мне кажется не очень эффективно.Имеет ли значение, использую ли я несколько команд записи подряд или собираю данные в массив байтов и отправляю их один раз?

Спасибо за любые ответы!

РЕДАКТИРОВАТЬ:Я добавил подсказку, что использую Indy 10, поскольку, похоже, в процедурах чтения и записи произошли серьезные изменения.

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

Решение

Буферизация записи отключена по умолчанию.Вы можете проверить буферизацию записи, чтобы узнать, активна ли она в вашем коде, протестировав свойство fConnection.IOHandler.WriteBufferingActive.

Что касается лучшего способа отправить целое число...«Это зависит» от вашего протокола и общих целей.В частности, используйте FConnection.IOHandler.Write(), поскольку существуют перегруженные методы для записи практически любого типа данных, включая целые числа.

Взято из IdIOHandler:

// Optimal Extra Methods
//
// These methods are based on the core methods. While they can be
// overridden, they are so simple that it is rare a more optimal method can
// be implemented. Because of this they are not overrideable.
//
//
// Write Methods
//
// Only the ones that have a hope of being better optimized in descendants
// have been marked virtual
procedure Write(const AOut: string; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure WriteLn(const AEncoding: TIdEncoding = enDefault); overload;
procedure WriteLn(const AOut: string; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure WriteLnRFC(const AOut: string = ''; const AEncoding: TIdEncoding = enDefault); virtual;
procedure Write(AValue: TStrings; AWriteLinesCount: Boolean = False; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure Write(AValue: Byte); overload;
procedure Write(AValue: Char; const AEncoding: TIdEncoding = enDefault); overload;
procedure Write(AValue: LongWord; AConvert: Boolean = True); overload;
procedure Write(AValue: LongInt; AConvert: Boolean = True); overload;
procedure Write(AValue: SmallInt; AConvert: Boolean = True); overload;
procedure Write(AValue: Int64; AConvert: Boolean = True); overload;
procedure Write(AStream: TStream; ASize: Int64 = 0; AWriteByteCount: Boolean = False); overload; virtual;

Другой вопрос, который у вас был, был: «Имеет ли это значение, использую ли я несколько команд записи в ряду или упаковываю вещи в массиве байтов и отправляю это один раз?» Для большинства случаев, да, это имеет значение.Для серверов с высокой нагрузкой вам придется больше участвовать в отправке байтов туда и обратно, но на этом уровне вы должны абстрагировать свои отправки в отдельный класс типа протокола, который формирует данные для отправки и отправляет их в виде всплеск и иметь протокол приема, который получает кучу данных и обрабатывает их как единое целое, вместо того, чтобы разбивать их на отправку/получение целого числа, символа, массива байтов и т. д.

В качестве очень грубого примера:

TmyCommand = class(TmyProtocol)
private
  fCommand:Integer;
  fParameter:String;
  fDestinationID:String;
  fSourceID:String;
  fWhatever:Integer;
public
  property Command:Integer read fCommand write fCommand;
  ...

  function Serialize;
  procedure Deserialize(Packet:String);
end;

function TmyCommand.Serialize:String;
begin
  //you'll need to delimit these to break them apart on the other side
  result := AddItem(Command) + 
            AddItem(Parameter) + 
            AddItem(DestinationID) + 
            AddItem(SourceID) + 
            AddItem(Whatever);
end; 
procedure TMyCommand.Deserialize(Packet:String);
begin
   Command := StrToInt(StripOutItem(Packet));
   Parameter := StripOutItem(Packet);
   DesintationID := StripOutItem(Packet); 
   SourceID := StripOutItem(Packet);
   Whatever := StrToInt(StripOutItem(Packet));
end;

Затем отправьте это через:

  FConnection.IOHandler.Write(myCommand.Serialize());

С другой стороны, вы можете получить данные через Indy, а затем

  myCommand.Deserialize(ReceivedData);

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

Я не знаком с Indy, но вы, возможно, захотите поискать в его API параметр TCP_NODELAY (возможно, вы захотите найти что-то подобное в исходном дереве Indy - нечувствительность к регистру для «задержки» должна помочь.)

Редактировать: Роб Кеннеди указал, что собственность, о которой я говорил, TIdIOHandlerSocket.UseNagle - Спасибо!

Проблема заложена в природе TCP.TCP гарантирует доставку данных в том же порядке, в котором они были отправлены, но нет гарантировать границы сообщений.Другими словами, операционная система источника, цели и любых маршрутизаторов на пути могут свободно объединять пакеты из соединения или фрагментировать их по своему желанию.Вы должны рассматривать передачу TCP как поток, нет как серия отдельных пакетов.Таким образом, вам придется реализовать механизм, с помощью которого вы либо разделяете отдельные сообщения (например, с помощью магического байта, который вы должны экранировать, если он также может встречаться в данных вашего сообщения), либо вы можете отправить длину следующего сообщения сначала, затем само сообщение.

Я всегда использовал UDP в сочетании с простой схемой подтверждения/повторной передачи, когда мне нужно было отправлять сообщения, где граница сообщения была важна, как в вашем случае.Возможно, стоит принять это во внимание.UDP гораздо лучше подходит для командных сообщений.

Похоже, вам нужно очистить буфер.Попробуй это:

TIdTCPConnection.FlushWriteBuffer;

Если вам не нужен буфер записи, используйте это:

TIdTCPConnection.CancelWriteBuffer;

Согласно справке, сначала вызывается ClearWriteBuffer для очистки буфера, а затем вызывается CloseWriteBuffer.

Лучший способ отправить целое число (с использованием Indy 10) — использовать TIdIOHandler.Write (согласно справке Indy 10 Write перегружен для обработки различных типов данных, включая целые числа).

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