【问题标题】:Delphi: (Indy) Send a header Record followed by a fileDelphi:(Indy)发送一个标题记录,后跟一个文件
【发布时间】:2012-07-18 13:02:28
【问题描述】:

我目前正在尝试使用 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);

我希望它执行一次,所以将来我可以先读取标题记录(已知大小),然后查看要遵循的内容/是否有任何内容并采取相应的行动..

我们将不胜感激!

感谢阅读, 阿德里安

【问题讨论】:

  • 或者,在记录中放置一个字节数组(或类似的东西?)来存储图像是更好的选择吗?问题是标题需要始终保持相同的大小,并且随着图像的变化,我需要一些方法来允许它..
  • 我会去序列化记录和数据到 XML,压缩和发送,那里有很多高性能的 XML 组件,我最喜欢的是omnixml code.google.com/p/omnixml
  • 我会问你在两个 OnExecute 事件的每一个中收到什么数据,因为这会提供一个关于正在发生的事情的线索——你不应该得到两个——但是因为你正在发送字符串数据错误,很难相信您在接收端看到的内容。您不能仅通过发送包含记录来发送字符串。 (想一想:您有至少 9 个字节的字符数据,但您注意到流大小为 8。)对于您的初始测试,忘记字符串并使用仅包含整数的记录类型,例如例子。将两条这样的记录放在流上,发送它,然后看看你收到了什么。

标签: delphi tcp stream record indy


【解决方案1】:

您的记录中包含非 ShortString 字符串,这一点从字符数据占用的字节数大于写入流中的字节数就可以看出 - 这意味着指针值 (2 x SizeOf(指针))被写入流而不是实际字符。因此,您必须手动序列化您的字符串,例如发送字符串长度后跟实际字符。如果您计划支持 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事件被多次触发,这是正常行为。该事件根本与数据传输无关。它在连接的生命周期内连续循环调用。退出事件处理程序后,如果客户端仍处于连接状态,则会立即再次触发它。这对于像您这样的基于消息的协议很有用,在这些协议中,事件被触发,读取一条等待数据到达的消息,退出,再次触发以读取下一条等待数据到达的消息,等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    相关资源
    最近更新 更多