Delphi: (Indy) Отправить запись заголовка, за которой следует файл

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

Таким образом, когда запись получена на сервере, если ее содержимое указывает, что за ней следует изображение, необходимо разделить данные записи и изображения.

var
Segment: TDPPSegment;
Segment2: TDPPSegment;
Buffer: TIdBytes;
Buffer2: TIdBytes;
Mem: TMemoryStream;
begin
if (Client.Connected) then begin

Segment.NameStr := 'Adrian';
Segment2.NameStr := 'Jon';

Mem := TMemoryStream.Create;
Mem.Write(Segment, SizeOf(Segment));
Mem.Write(Segment2, SizeOf(Segment2));

//The Size of the stream is 8 bytes here!
Client.IOHandler.Write(Mem, 0, False);

конец;

Чтобы смоделировать «файл изображения», я просто хочу послать ДВЕ записи последовательно; это цель. Обратите внимание: я отправляю весь поток памяти сразу (!), А НЕ записываю за записью. Но что интересно, сервер дважды запускает событие OnExecute!

var
Buffer: TIdBytes;
Segment: TDPPSegment;
Mem: TMemoryStream;
begin
Mem := TMemoryStream.Create;
AContext.Connection.IOHandler.ReadStream(Mem, SizeOf(TDPPSegment), False);

//Incoming stream size is FOUR bytes but TWICE!
Mem.Position := 0;
Mem.Read(Segment, SizeOf(TDPPSegment));
Showmessage(Segment.NameStr);

Я хочу, чтобы он выполнялся один раз, поэтому в будущем я могу сначала прочитать запись заголовка (известный размер), а ЗАТЕМ посмотреть, что / если что-то будет следовать, и действовать соответственно ..

Помощь будет принята с благодарностью!

Спасибо за чтение, Адриан


person Adrian    schedule 18.07.2012    source источник
comment
Или это лучшая альтернатива помещению байтового массива в запись (или чего-то подобного?) Для хранения изображения? Проблема в том, что заголовки должны всегда быть одного и того же размера, и, поскольку изображения меняются, мне нужен способ разрешить это.   -  person Adrian    schedule 18.07.2012
comment
Я бы пошел на сериализацию записи и данных в XML, сжатие и отправку, есть много высокопроизводительных XML-компонентов, я предпочитаю omnixml code.google.com/p/omnixml   -  person    schedule 18.07.2012
comment
Я бы спросил, какие данные вы получаете в каждом из двух событий OnExecute, поскольку это даст представление о том, что происходит - у вас не должно быть двух - но поскольку вы отправляете строковые данные неправильно, это будет сложно доверять тому, что вы видите на принимающей стороне. Вы не можете отправлять строки, просто отправив содержащую запись. (Подумайте об этом: у вас не менее 9 байтов символьных данных, но вы заметили, что размер потока равен 8.) Для начального теста забудьте о строках и используйте тип записи, который содержит только Integer, для пример. Поместите две такие записи в поток, отправьте их и посмотрите, что вы получите.   -  person Rob Kennedy    schedule 18.07.2012


Ответы (1)


Ваши записи содержат в себе строки, отличные от ShortString, о чем свидетельствует тот факт, что количество байтов, занятых символьными данными, больше, чем количество байтов, записываемых в поток - это означает, что значения указателя (2 x SizeOf (Pointer)) записываются в поток вместо фактических символов. Таким образом, вам придется сериализовать свои строки вручную, например, отправив длину строки, за которой следуют фактические символы. И если вы планируете поддерживать Delphi 2009+, вы должны учитывать Unicode, поэтому вам следует кодировать строки перед их передачей, а затем декодировать их на принимающей стороне.

Записи полезны для организации ваших данных в памяти, но обычно не очень полезны для передачи данных по сети, если только записи не содержат только типы POD (и строка не квалифицируется как это).

Попробуй это:

procedure WriteStrToIO(IO: TIdIOHandler; const S: String);
var
  Buf: TIdBytes;
  Len: Integer;
begin
  Buf := ToBytes(S, IndyUTF8Encoding);
  Len := Length(Buf);
  IO.Write(Len); 
  if Len > 0 then IO.Write(Buf); 
end;

var 
  Len: Integer;
  Buf: TIdBytes; // or whatever you want to use...
begin 
  if Client.Connected then
  begin 
    WriteStrToIO(Client.IOHandler, 'Adrian');

    Buf := ...; // secondary data
    Len := Length(Buf);
    Client.IOHandler.Write(Len);
    if Len > 0 then
      Client.IOHandler.Write(Buf);

    ...
  end;
end;

.

var 
  NameStr: String; 
  Buf: TIdBytes;
begin   
  with AContext.Connection.IOHandler do
  begin
    NameStr := ReadString(ReadInteger, IndyUTF8Encoding); 

    // read secondary data ...
    ReadBytes(Buf, ReadInteger);
  end;

  // ShowMessage() is not thread-safe!
  Windows.MessageBox(0, PChar(NameStr), 'NameStr', MB_OK); 
end;

Что касается события OnExecute, которое запускается несколько раз, это нормальное поведение. Это событие вообще не связано с передачей данных. Он вызывается в непрерывном цикле на время жизни соединения. После выхода из обработчика событий он немедленно запускается снова, если клиент все еще подключен. Это полезно для протоколов на основе сообщений, таких как ваш, где событие запускается, считывает одно сообщение, ожидающее прибытия данных, завершается, запускается снова, чтобы прочитать следующее сообщение, ожидающее прибытия данных, и так далее.

person Remy Lebeau    schedule 18.07.2012