【问题标题】:DataSnap XE2 and TStream method parametersDataSnap XE2 和 TStream 方法参数
【发布时间】:2012-08-24 21:08:37
【问题描述】:

我正在使用 TCP/IP 协议在 Delphi XE2 中开发 DataSnap 项目,该协议需要将二进制数据流作为方法参数传递给服务器。我遇到的问题是流内容的大小限制似乎约为 32 KB。超出此限制,服务器接收到的流为空。如果我传递其他方法参数,它们会完好无损地到达,所以这似乎是参数级别的问题。

以下是 DataSnap 服务类的声明方式:

  TDataSnapTestClient = class(TDSAdminClient)
  private
    FSendDataCommand: TDBXCommand;
  public
    constructor Create(ADBXConnection: TDBXConnection); overload;
    constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
    destructor Destroy; override;
    procedure SendData(Data: TStream);
  end;

至少根据 Jim Tierney 的文章,我使用的方法应该有效。也就是说,自 Delphi 2009 以来,显然发生了一些更改,破坏了 Jim Tierney 的示例代码。

DataSnap Server Method Stream Parameters

任何关于如何解决此问题的想法将不胜感激。

【问题讨论】:

  • 我在调试器中跟踪了客户端代码,发现问题出在 TDBXRowBuffer.WriteBytes 中。看起来这是一个已知问题:qc.embarcadero.com/wc/qcmain.aspx?d=90995
  • 您是仅在将数据推送到 DS 服务器时才看到问题,还是在拉取数据时也看到了问题?我通常使用TStream 从 DS 服务器中提取几个兆字节流。我使用 Ethereal 观察传输的数据包,它以 32k 块传输二进制数据,直到收到所有块。
  • 仅供参考 - Embarcadero QC 90995 现已以“按设计”解决方案关闭。处理 DataSnap 流参数的官方正确方法是检查流 Size 属性。如果值为 -1,则需要读取流,直到 Read 方法返回零结果。感谢 James L. 指出这个问题的解决方案。

标签: delphi stream datasnap


【解决方案1】:

DataSnap 以 32k 块传输数据。在所有块重新组装之前,接收端无法知道将接收多少字节。接收到所有数据后,DataSnap 不会设置接收数据的TStream 的大小,因此在将其移动到另一个知道流中有多少字节的流之前,您无法使用它。

我知道从 DataSnap 服务器提取 32k+ 与将 32k+ 推送到 DataSnap 服务器不同,但这也可能对您有用。在 DataSnap 服务器完成接收数据后,尝试通过此代码运行TStream

procedure CopyStreamToMemoryStream(const ASource: TStream; var ADest: TMemoryStream; const Rewind: Boolean = True);
const
  LBufSize = $F000;
var
  LBuffer: PByte;
  LReadCount: Integer;
begin
  GetMem(LBuffer, LBufSize);
  ADest.Clear;
  try
    repeat
      LReadCount := ASource.Read(LBuffer^, LBufSize);
      if LReadCount > 0 then
        ADest.WriteBuffer(LBuffer^, LReadCount);
    until LReadCount < LBufSize;
  finally
    FreeMem(LBuffer, LBufSize);
  end;
  if Rewind then
    ADest.Seek(0, TSeekOrigin.soBeginning);
end;

我不记得我在哪里找到了这个代码(几年前),所以我不能在应得的地方给予信任,但它多年来一直可靠地为我工作。

【讨论】:

  • 谢谢!这解决了问题。在服务器端,我使用 AnyDAC 将流内容传递给存储过程。显然,用于从流中读取的逻辑取决于流大小,而不是检查流 Read 方法返回的字节数。
【解决方案2】:

我开始思考这个问题,突然想到将数据传输到另一个内存流只会浪费内存,尤其是在文件非常大的情况下。我们需要做的就是计算字节数并设置流大小,对吧?!

procedure FixStream(const AStream: TStream);
const
  LBufSize = $F000;
var
  LBuffer: PByte;
  LReadCount, StreamSize: Integer;
begin
  GetMem(LBuffer, LBufSize);
  try
    StreamSize := 0;
    repeat
      LReadCount := AStream.Read(LBuffer^, LBufSize);
      Inc(StreamSize, LReadCount);
    until LReadCount < LBufSize;
    AStream.Size := StreamSize;
  finally
    FreeMem(LBuffer, LBufSize);
  end;
end;

你想试试吗?我现在无法测试代码,否则我会……

【讨论】:

  • 这应该是对您其他答案的编辑。此外,您对 const 和 var 对引用类型的含义有一个明显的误解。
  • 这种方法行不通,因为 TDBXStreamReaderStream 中的底层 DataSnap 流实现旨在以块的形式读取流内容,并且无法存储流的全部内容。可以创建一个流包装类来缓冲完整的流,但这并不能真正改善流复制方法。
  • @DavidHeffernan - 另一个答案已经被接受为正确答案,所以我认为最好保持原样。感谢您对 const 与 var 的评论。 'const' 在这种情况下更合适。
  • @DavidTaylor - 由于这不起作用,我们可以删除答案,但您的评论增加了有用的见解,所以也许我们应该留下它......
  • const 也没什么用处
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多