0

我的任务是处理使用 TClientSocket 从返回 XML 的 TCP/IP 服务器请求数据的旧 D6 应用程序,我需要与新的第 3 方服务器集成,并且在接收完整数据时遇到间歇性问题在服务器的响应中发送。发生这种情况时,尽管 XML 非常小,大约 1.76 KB,但我通过多个 OnRead 事件获取 XML 字符串。

响应的结构是前四个字节返回表示:

字节位置、类型/用途:

0 - 0x02 (STX)
1 - Length, LSB
2 - Length
3 - Length, MSB
Bytes 4+ are the xml payload

然而,我之前的程序员只是简单地使用了 Socket.ReceiveText,因为接收到的现有逻辑的所有响应都非常小,在大多数情况下远低于 200 字节......基本上是成功确认或错误数据。

考虑到我是分块获得的,有人会非常介意让我知道如何成功地使用响应吗?即使我经常使用 Delphi,我也没有经常使用 TClientSocket/TServerSocket,而且我无法更改为我更熟悉的东西。

提前致谢。

4

1 回答 1

2

在这种情况OnRead下,读取套接字上当前可用的任何字节并将它们附加到缓冲区。然后,您可以遍历该缓冲区,仅提取完整的 XML 消息并根据需要对其进行处理,将不完整的 XML 消息留在缓冲区中,以便在以后的OnRead事件中完成它们。

例如:

type
  XmlHdr = packed record
    Stx: Byte;
    XmlLen: array[0..2] of Byte;
  end;

procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
  // allocate the receive buffer
  Socket.Data := TMemoryStream.Create;
end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  // free the receive buffer
  TMemoryStream(Socket.Data).Free;
  Socket.Data := nil;
end;

procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
var
  Strm: TMemoryStream;
  RecvLen: Integer;
  StrmSize: Int64;
  Ptr: PByte;
  hdr: XmlHdr;
  xml: AnsiString;
begin
  Strm := TMemoryStream(Socket.Data);

  // check how many bytes are currently available on the socket
  RecvLen := Socket.ReceiveLength;
  if RecvLen <= 0 then Exit;

  // read the bytes, appending them to the end of the buffer

  StrmSize := Strm.Size;
  Strm.Size := StrmSize + RecvLen;

  Ptr := PByte(Strm.Memory);
  Inc(Ptr, StrmSize);

  RecvLen := Socket.ReceiveBuf(Ptr^, RecvLen);
  if RecvLen <= 0 then
  begin
    Strm.Size := StrmSize;
    Exit;
  end;

  Strm.Size := StrmSize + RecvLen;

  // loop through the buffer processing only complete XML messages

  Strm.Position := 0;    
  while (Strm.Size - Strm.Position) >= SizeOf(hdr) do
  begin
    // make sure the next byte starts a new header
    Strm.ReadBuffer(hdr.Stx, 1);
    if Hdr.Stx <> $2 then Continue;    

    // read the header's XML length
    Strm.ReadBuffer(hdr.XmlLen[0], 3);

    {
    0 - Length, LSB
    1 - Length
    2 - Length, MSB
    }
    RecvLen := (Integer(hdr.XmlLen[2]) shl 16) or (Integer(hdr.XmlLen[1]) shl 8) or Integer(hdr.XmlLen[0]);

    // check if the complete XML has been received
    if (Strm.Size - Strm.Position) < RecvLen then
    begin
      // nope, keep waiting
      Strm.Seek(-SizeOf(hdr), soCurrent);
      Break;
    end;

    // extract the complete XML
    SetLength(xml, RecvLen);
    Strm.ReadBuffer(PAnsiChar(xml)^, RecvLen);    

    // process xml as needed...
  end;

  if strm.Position > 0 then
  begin
    // remove consumed bytes from the buffer and compact it
    StrmSize := Strm.Size - Strm.Position;
    if StrmSize = 0 then
    begin
      Strm.Clear;
    end else
    begin
      Ptr := PByte(Strm.Memory);
      Inc(Ptr, Strm.Position);
      Move(Ptr^, Strm.Memory^, StrmSize);
      Strm.Size := StrmSize;
    end;
  end;
end;
于 2015-06-25T22:18:49.673 回答