使用Indy的HTTP连续打包流

him*_*elf 6 delphi indy indy10 idhttp

我有一个JSON-RPC服务,其中一个请求返回一个连续的JSON对象流.

即:

{id:'1'}
{id:'2'}
//30 minutes of no data
{id:'3'}
//...
Run Code Online (Sandbox Code Playgroud)

当然,没有内容长度,因为流是无穷无尽的.

我正在使用自定义TStream后代来接收和解析数据.但内部TIdHttp缓冲数据并且RecvBufferSize在收到字节之前不会将其传递给我.

这导致:

{id:'1'} //received
{id:'2'} //buffered by Indy but not received
//30 minutes of no data
{id:'3'} //this is where Indy commits {id:'2'} to me
Run Code Online (Sandbox Code Playgroud)

显然这不会做,因为30分钟前重要的信息应该在30分钟前送达.

我希望Indy能做套接字的工作:如果有可用数据则立即读取RecvBufferSize并立即返回.

我从2005年发现了这个讨论,其中一些可怜的灵魂试图向Indy开发者解释这个问题,但他们并不了解他.(阅读它;这是一个悲伤的景象)

无论如何,他通过编写自定义IOHandler后代来解决这个问题,但那是在2005年,也许今天有一些现成的解决方案?

him*_*elf 2

虽然使用 TCP 流是一种选择,但最终我采用了编写自定义TIdIOHandlerStack后代的原始解决方案。

动机是,使用 TIdHTTP,我知道什么不起作用,只需要修复它,而切换到较低级别的 TCP 意味着可能会出现新问题。

这是我正在使用的代码,我将在这里讨论关键点。

NewTIdStreamIoHandler必须继承自TIdIOHandlerStack.

需要重写两个函数:ReadBytesReadStream

function TryReadBytes(var VBuffer: TIdBytes; AByteCount: Integer;
  AAppend: Boolean = True): integer; virtual;
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1;
  AReadUntilDisconnect: Boolean = False); override;
Run Code Online (Sandbox Code Playgroud)

两者都是经过修改的 Indy 函数,可以在 中找到IdIOHandler.TIdIOHandler。该ReadBytes 子句while必须替换为单个ReadFromSource()请求,以便TryReadBytes在一次性读取最多 AByteCount 字节后返回。

基于此,ReadStream必须处理 AByteCount (>0, <0) 和 ReadUntilDisconnect (true, false) 的所有组合,以循环读取然后写入从套接字到达的数据流块。

请注意,ReadStream如果套接字中只有部分请求的数据可用,即使在此流版本中也不需要提前终止。它只需立即将该部分写入流中,而不是将其缓存在 中FInputBuffer,然后阻塞并等待下一部分数据。