2015-06-25 1 views
0

Мне поручено работать с устаревшим приложением D6, которое использует TClientSocket для запроса данных с сервера TCP/IP, который возвращает XML, для чего мне необходимо интегрироваться с новым сторонним сервером, и у меня возникает прерывистая проблема с получением полные данные, отправленные в ответ сервера. Когда это произойдет, я получаю XML-строку над несколькими событиями OnRead, несмотря на то, что XML довольно маленький, около 1,76 КБ.Как получить данные сокетов из нескольких чтений?

Структура ответа является то, что первые четыре байта возвращающие указывают:

Byte Позиция, тип/назначение:

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

Однако программист до меня просто использовали Socket.ReceiveText, потому что все ответ, полученный для существующей логики, был очень мал, а в большинстве случаев - менее 200 байт ... по существу, подтверждение успеха или данные об ошибках.

Может, кто-то умнит меня, чтобы я понял, как я могу успешно использовать ответ, учитывая, что я получаю его в кусках? Я не использовал TClientSocket/TServerSocket, даже когда я регулярно использовал Delphi, и я не могу изменить то, с чем я больше знаком.

Заранее спасибо.

ответ

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

Remy! Теперь это имя, которого я не видел во многих лунах. Спасибо за ответ и понимание. Быстрый вопрос: как провести различие между одним или несколькими событиями чтения, связанными с ответом одного сервера от другого, учитывая асинхронный характер? Например, где вы указываете в своем примере, где должен обрабатываться xml, что нужно делать там, чтобы на следующем прочитанном событии я знал бы, что это для совершенно нового отдельного ответа/ответа? Благодаря! – LeeboJenkowitz

+0

Обычно вы не используете такую ​​асинхронную модель чтения для системы команд/ответов, если сами ответы не асинхронны (одновременно могут выполняться несколько команд, и ответы могут поступать в любом порядке), или сервер может отправлять незапрашиваемые сообщения между запрошенными ответами. В таких случаях ответы обычно содержат информацию, чтобы связать их с исходной командой. Если нет, вы можете, по крайней мере, сохранить заполненные XML-сообщения в списке FIFO, и выпустить выпадающий список команд из любых XML-сообщений, которые он ожидает в качестве ответа. –

+0

Спасибо за продолжение. Я считаю, что сейчас у меня хорошее понимание. – LeeboJenkowitz