2015-06-25 66 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