简体   繁体   中英

How do I receive socket data from multiple reads?

I am tasked with working on a legacy D6 application that uses TClientSocket to request data from a TCP/IP Server which returns XML for which, I am required to integrate with a new 3rd party server and I am having an intermittent problem receiving the full data sent in the Server's response. When this happens I get the XML string over multiple OnRead events despite that the XML is pretty small, about 1.76 KB.

The structure of the response is that the first four bytes return indicate:

Byte Position, type/purpose:

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

However, the programmer before me simply used Socket.ReceiveText because all of the response being received for existing logic was very small, well under 200 bytes in most cases...essentially success confirmation or error data.

Would someone mind terribly giving me an idea of how I can successfully consume the response taking into account that I'm getting it in chunks? I've not used TClientSocket/TServerSocket much even when I was using Delphi regularly and I cannot change to something I am more familiar with.

Thanks in advance.

In the OnRead event, read whatever bytes are currently available on the socket and append them to a buffer. Then you can loop through that buffer extracting only complete XML messages and process them as needed, leaving incomplete XML messages in the buffer so they can be completed in later OnRead events.

For example:

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM