2

我正在使用 Delphi 中的 TIdTCPClient/TIdTcpServer indy 组件编写客户端-服务器应用程序。

数据传输通常工作正常,但我经常从服务器读取错误的数据;我得到以前请求的答案,而不是当前的。

在调试期间,两个应用程序都在本地工作,因此在传输过程中数据不会丢失。

超时为 1000-3000 毫秒,这足以避免在收到第一个请求的答案之前发送第二个请求。

我使用简单的数据格式:前 4 个字节为数据包长度,其余为该长度的二进制数据。

服务器端代码是(简化为仅发送字符串;我也以相同的方式使用二进制缓冲区,但此代码更易于理解和检查):

Var
  lng: LongInt;
  ib: TIdBytes;
begin
  // Prepare data to send:
  lng:=length(s);// s is an AnsiString to be sent
  SetLength(ib,lng+4);
  Move(lng,ib[0],4);
  Move(s[1],ib[4],length(s));
  // Send:
  AContext.Connection.IOHandler.WriteDirect(ib);
end;

发送请求的客户端代码是相同的(在最后一行调用 TIdTcpClient.IOHandler.WriteDirect() )。读取服务器答案的客户端代码是:

Var 
  ib: TIdBytes;
  size,done,lng: LongInt;
begin
  Result:=false;
  //  answer length:
  try
    SetLength(ib,0);
    tcp.IOHandler.ReadBytes(ib,4,false);
    Move(ib[0],size,4);
    if length(ib)<0 then Exit;// wrong data
  except
    on E: Exception do;// code skipped
  end;
  //  read answer body:
  done:=0;
  b.Clear;// b is my buffer, TStream descendant
  while done<size do
    begin
    lng:=Min(size-done,MaxBlockSize);
    // read:
    SetLength(ib,0);// to be sure
    tcp.IOHandler.ReadBytes(ib,lng,false);
    if length(ib)=0 then Exit;// error reading
    // append my buffer:
    b.Wr(ib[0],length(ib));
    // progress:
    Inc(done,length(ib));
    end;
end;

数据交换顺序为:

  1. 客户端向服务器发送请求,

  2. 服务器读取请求并将应答发送回客户端,

  3. 客户阅读答案。

第 3 步出现错误数据。

也许我做错了什么?

在向服务器发送请求以清除传入缓冲区之前,我已经尝试过 ReadBytes() ,但这也无济于事,就像我尝试过的许多其他事情一样......

现在我只是没有想法:(

4

1 回答 1

5

您的 I/O 逻辑比它需要的复杂得多,尤其是在客户端。您正在手动执行 Indy 可以自动为您执行的操作。

在客户端,由于您将数据保存到 TStream 中,您可以让 Indy 直接将数据读入 TStream:

begin
  ...
  b.Clear;// b is my buffer, TStream descendant
  // ReadStream() can read a '<length><bytes>' formatted
  // message.  When its ASize parameter is -1 and its
  // AReadUntilDisconnect parameter is False, it reads
  // the first 4 or 8 bytes (depending on the LargeStream
  // property) and interprets them as the byte count,
  // in network byte order...
  tcp.IOHandler.RecvBufferSize := MaxBlockSize;
  tcp.IOHandler.LargeStream := False; // read 4-byte length
  // read answer:
  try
    tcp.IOHandler.ReadStream(b, -1, false);
  except
    on E: Exception do begin
      // the socket is now in an indeterminate state.
      // You do not know where the reading left off.
      // The only sensible thing to do is disconnect
      // and reconnect...
      tcp.Disconnect;
      ...
    end;
  end;
  ...
end;

在服务器端,您可以通过两种不同的方式发送与上述代码兼容的消息:

var
  lng: LongInt;
  ib: TIdBytes;
begin
  // Prepare data to send:
  // s is an AnsiString to be sent
  lng := Length(s);
  SetLength(ib, lng);
  Move(PAnsiChar(s)^, PByte(ib)^, lng);
  // Send:
  AContext.Connection.IOHandler.Write(lng); // send 4-byte length, in network byte order
  AContext.Connection.IOHandler.Write(ib); // send bytes
end;

或者:

var
  strm: TIdMemoryBufferStream;
begin
  // Prepare data to send:
  // s is an AnsiString to be sent
  strm := TIdMemoryBufferStream.Create(PAnsiChar(s), Length(s));
  try
    // Send:
    // Write(TStream) can send a '<length><bytes>' formatted
    // message.  When its ASize parameter is 0, it sends the
    // entire stream, and when its AWriteByteCount parameter
    // is True, it first sends the byte count as 4 or 8 bytes
    // (depending on the LargeStream property), in network
    // byte order...
    AContext.Connection.IOHandler.LargeStream := False; // send 4-byte lengtb
    AContext.Connection.IOHandler.Write(strm, 0, True);
  finally
    strm.Free;
  end;
end;

如您所见,此代码发送的消息类型与您最初发送的消息类型相同,改变的是管理消息的代码。此外,它强制消息字节计数以网络字节顺序发送,而您以主机字节顺序发送它。多字节整数应尽可能以网络字节顺序发送,以保持一致性和多平台兼容性。

于 2015-01-16T16:46:56.240 回答