Writing stream data & XML data to TIdTCPClient and TIdIOHandlerStream (Delphi 2006 & Indy10)

Posted on 2006-06-17
Last Modified: 2008-01-09
I am trying to implement a client connection to a server (TCP).  I know the server is working because there's a VB app that makes an XMLQuery and receives a response.

I need to do the same with my Delphi 2006 Architect & Indy 10 application.  Assume I know VERY little in this area and you will be close to the truth.  

I was told by the folks who have the XML database server that I first have to send the size of the query in network byte order, get an Ack or Nak back, then send the query itself, and then I'll get an Ack or Nak, then the actual XML response.  I'm getting an AV on the first IOHandlerStream.Write.  Obviously, I don't understand what I'm doing.  This must be installed later today. HELP!!  

Here's my code in the FormActivate event.

procedure TForm1.FormActivate(Sender: TObject);
  s: string;
  sXML: string;
  iSize: longint;
  iLong: longint;
  pLong, pXML: PChar;
    with Client do
        sXML := '<?xml version="1.0" encoding="UTF-8"?>' + #13#10 +
          '  <PublicSafetyEnvelope version="1.0">' + #13#10 +
          '    <MessageIdentification/> <From/> <To/> <Creation/>' + #13#10 +
          '    <PublicSafety id="">' + #13#10 +
          '      <Query>' + #13#10 +
          '        <MainNamesTable>' + #13#10 +
          '          <NameNumber search_type="equal_to">9</NameNumber>' + #13#10 +
          '        </MainNamesTable>' + #13#10 +
          '      </Query>' + #13#10 +
          '    </PublicSafety>' + #13#10 +
          '  </PublicSafetyEnvelope>';
        pXML := PChar(sXML);
        iSize := SizeOf(pXML);
        iLong := htonl(iSize);
        pLong := PChar(iLong);
        s := IOHandlerStream.ReadLn;
Question by:bjones8888
  • 2

Author Comment

ID: 16928142
I've made progress, but still don't quite have it.  Now I get a response without errors.  And it even appears to be returning the right NUMBER of characters, but they're all spaces.  If I take what the VB app returns and paste it into notepad & save it, the file is 1518 bytes long.  My app now returns 1479 characters, but they're all spaces or non-displayable characters.  Any help?  Still 500 points to whomever can correct the error of my ways!

procedure TForm1.FormActivate(Sender: TObject);
  s: string;
  sXML: string;
  sStrings: TStringList;
  iSize: integer;
  iLong, i: integer;
    with Client do
        sXML := '';
        sStrings := TStringList.Create;
        sStrings.Add('<?xml version="1.0" encoding="UTF-8"?>');
        sStrings.Add('  <PublicSafetyEnvelope version="1.0">');
        sStrings.Add('    <MessageIdentification/> <From/> <To/> <Creation/>');
        sStrings.Add('    <PublicSafety id="">');
        sStrings.Add('      <Query>');
        sStrings.Add('        <MainNamesTable>');
        sStrings.Add('          <NameNumber search_type=''equal_to''>9</NameNumber>');
        sStrings.Add('        </MainNamesTable>');
        sStrings.Add('      </Query>');
        sStrings.Add('    </PublicSafety>');
        sStrings.Add('  </PublicSafetyEnvelope>');
        for i := 0 to sStrings.count - 1 do
          sXML := sXML + sStrings[i];
        iSize := Length(sXML);;
        IOHandlerStack.Write(iSize, True);       ' theoretically this puts it into network byte order and writes the 4 byte header
        s := IOHandlerStack.AllData;
    iLong := Length(s);
    ShowMessage('Returned ' + IntToStr(iLong) + ' bytes. Here they are:' + Chr(13) + Chr(10) + s);

For what it's worth, here's the VB app code that was given to me.

'/////  This is the code that runs when the button on the form is clicked.  
    Private Sub submitButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles submitButton.Click
        Dim myQuery As XMLQuery
        myQuery = New XMLQuery

        myQuery.Hostname = hostnameTextBox.Text     ' this holds the tcp/ip address
        myQuery.Port = Long.Parse(portTextBox.Text)   ' this holds the port number

        ' queryRichTextBox contains the same text I'm putting into sXML above
        ' responseRichTextBox receives the result of the query
        responseRichTextBox.Text = myQuery.SubmitQuery(queryRichTextBox.Text)
    End Sub

'//// this is XMLQuery.vb
        '-- Send the 4 byte header
        Dim LengthBuffer = BitConverter.GetBytes(LengthInNetworkByteOrder)
        ClientSocket.Send(LengthBuffer, 4, SocketFlags.None)

        '-- Send the data
        BytesToSend = New System.Text.ASCIIEncoding().GetBytes(Msg)

        '-- Read the ACK (\006) or NAK (\025)
        Dim AckOrNak(1) As Byte
        ClientSocket.Receive(AckOrNak, 1, SocketFlags.None)
        If AckOrNak(0) <> 6 And AckOrNak(0) <> 25 Then
            '-- Invalid ACK/NAK received
            MessageBox.Show("Invalid ACK/NAK received: " + AckOrNak(0).ToString())
            Return ""
        ElseIf AckOrNak(0) = 25 Then
            '-- NAK received
            MessageBox.Show("NAK received from XML Query Server")
            Return ""
        End If

        '-- Read the 4 byte response length into LengthBuffer
        Dim Numbytes As Int32
        Numbytes = ClientSocket.Receive(LengthBuffer, 4, SocketFlags.None)

        '-- Convert the Response length into the host's byte order
        Dim ResponseLengthInNetworkByteOrder = BitConverter.ToInt32(LengthBuffer, 0)
        Dim ResponseLength = IPAddress.NetworkToHostOrder(ResponseLengthInNetworkByteOrder)

        '-- Receive the message
        Dim ResponseBuffer(50) As Byte
        Dim BytesSoFar As Int32
        Dim NumBytesToRead As Int32 = Numbytes
        ResponseString = ""

        While NumBytesToRead > 0

            Numbytes = ClientSocket.Receive(ResponseBuffer, NumBytesToRead, SocketFlags.None)
            If Numbytes > 0 Then
                ReDim Preserve ResponseBuffer(Numbytes - 1)
                '-- Convert the byte array into an ASCII string and add it to ResponseString
                ResponseString += System.Text.ASCIIEncoding.ASCII.GetString(ResponseBuffer)
                BytesSoFar += Numbytes
            End If

            If ResponseLength < BytesSoFar + ResponseBuffer.Length Then
                NumBytesToRead = ResponseLength - BytesSoFar
                NumBytesToRead = ResponseBuffer.Length
            End If

        End While

        '-- Shutdown and Close the socket

        Return ResponseString
    End Function
End Class

Author Comment

ID: 17008207
Here's the solution.  The problem was that the ACK characters in response to the write statements weren't being removed from the stream.  For some reason those characters were causing the ShowMessage statement to display as all spaces, but the correct response was actually in the response stream.

After stripping those off the beginning of the stream, the display was correct.

Accepted Solution

kodiakbear earned 0 total points
ID: 17014003
Closed, 500 points refunded.
Experts Exchange Moderator

Featured Post

ScreenConnect 6.0 Free Trial

Explore all the enhancements in one game-changing release, ScreenConnect 6.0, based on partner feedback. New features include a redesigned UI, app configurations and chat acknowledgement to improve customer engagement!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In a recent question ( here at Experts Exchange, a member asked how to add page numbers to a PDF file using Adobe Acrobat XI Pro. This short video Micro Tutorial sh…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

825 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question