简体   繁体   English

Delphi:(Indy)发送头记录,后跟文件

[英]Delphi: (Indy) Send a header Record followed by a file

I am currently trying to use Indy to write a custom higher level protocol on top of TCP. 我目前正在尝试使用Indy在TCP之上编写自定义更高级别的协议。 Essentially, I want to send a record across (using a stream) but this record can indicate that x bytes to follow will be an image file. 本质上,我想跨(使用流)发送一条记录,但是该记录可以表明要跟随的x字节将是一个图像文件。

As such, when the record is recieved on the server, if it's contents indicate it's followed by an image, it needs to separate the record and image data. 这样,当在服务器上接收到记录时,如果记录的内容指示其后跟有图像,则需要将记录和图像数据分开。

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);

end; 结束;

To simulate the 'image file' I simply want to send TWO records consecutively; 为了模拟“图像文件”,我只想连续发送两个记录。 thats the aim. 那就是目标。 Notice here I am sending the whole memory stream at once (!) and NOT record by record. 请注意,这里我是一次发送整个内存流(!),而不是逐条记录。 But interestingly, the server runs the OnExecute event twice! 但是有趣的是,服务器运行了两次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);

I want it to execute once, so in the future I can first read the header record (known size) and THEN see what/if anything is to follow and act accordingly.. 我希望它执行一次,所以将来我可以先读取标头记录(已知大小),然后查看将要遵循的内容/并采取相应的措施。

Help would be greatly appreciated! 帮助将不胜感激!

Thanks for reading, Adrian 感谢您的阅读,阿德里安

Your records contain non-ShortString strings in them, as evident by the fact that the number of bytes occupied by the character data is larger than the number of bytes being written into the stream - meaning that pointer values (2 x SizeOf(Pointer)) are being written to the stream instead of the actual characters. 您的记录中包含非ShortString字符串,这可以通过以下事实证明:字符数据占用的字节数大于写入流中的字节数-这意味着指针值(2 x SizeOf(Pointer))被写入流而不是实际字符。 As such, you will have to serialize your strings manually, such as by sending the string length followed by the actual characters. 因此,您将必须手动序列化字符串,例如发送字符串长度,然后发送实际字符。 And if you plan on supporting Delphi 2009+, you have to take Unicode into account, so you should encode the strings before transmitting them, and then decode them on the receiving end. 而且,如果计划支持Delphi 2009+,则必须考虑Unicode,因此在传输字符串之前应该对其进行编码,然后在接收端对其进行解码。

Records are useful for organizing your data in memory, but usually not very ueful for transmitting data over a network, unless the records contain only POD types (and a string does not qualify as that). 记录对于组织内存中的数据很有用,但通常对于通过网络传输数据不是很方便,除非记录仅包含POD类型(并且字符串不符合此条件)。

Try this: 尝试这个:

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;

As for the OnExecute event being triggered multiple times, that is normal behavior. 至于OnExecute事件被多次触发,这是正常现象。 That event is not tied to data transmissions at all. 该事件根本与数据传输无关。 It is called in a continuous loop for the lifetime of the connection. 在连接的生命周期中,在连续循环中调用它。 Once you exit the event handler, it is triggered again immediately if the client is still connected. 退出事件处理程序后,如果客户端仍处于连接状态,则会立即再次触发它。 This is useful for message-based protocols like yours, where the event gets triggered, reads a single message waiting for data to arrive, exits, gets triggered again to read the next message waiting for data to arrive, and so on. 这对于像您这样的基于消息的协议很有用,在该协议中,事件被触发,读取一条消息等待数据到达,退出,再次触发以读取下一条消息等待数据到达,等等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM