2015-06-25 147 views
0

我与使用Tclientsocket构件实现从TCP/IP服务器返回的XML的哪个请求数据的遗留D6应用程序的工作任务,我需要一个新的第三方服务器整合和我有间歇性的问题接受在服务器的响应中发送完整的数据。发生这种情况时,尽管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,我不能改变我更熟悉的东西。

在此先感谢。

回答

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; 
+0

雷米!这是我在许多卫星上没有看到的名字。感谢您的回答和见解。快速提问:如何区分一个或多个读取事件与一个服务器响应相关联,而另一个服务器响应是异步本质?例如,在你的示例中,在哪里应该处理xml,应该在那里做什么,以便在下次读取事件时我会知道它是用于全新的单独/响应?谢谢! – LeeboJenkowitz

+0

对于命令/响应系统,通常不会使用这种异步读取模型,除非响应本身是异步的(多个命令可以同时进行并且响应可以按任意顺序到达),或者服务器可以在请求的响应之间发送未经请求的消息。在这种情况下,响应通常包含将它们链接到原始命令的信息。如果没有,您至少可以将已完成的XML消息保存到FIFO列表中,并让您的命令发布者将其预期的任何XML消息作为响应提取出来。 –

+0

感谢您的跟进。我相信我现在有一个很好的理解。 – LeeboJenkowitz