We help IT Professionals succeed at work.

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

bjones8888
bjones8888 asked
on
1,386 Views
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);
var
  s: string;
  sXML: string;
  iSize: longint;
  iLong: longint;
  pLong, pXML: PChar;
begin
    with Client do
    begin
      Connect;
      try
        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);
        IOHandlerStream.Write(pLong^);
        IOHandlerStream.Write(pXML^);
        s := IOHandlerStream.ReadLn;
        ShowMessage(s);
      finally
        Disconnect;
      end;
    end;
end;
Comment
Watch Question

bjones8888President

Author

Commented:
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);
var
  s: string;
  sXML: string;
  sStrings: TStringList;
  iSize: integer;
  iLong, i: integer;
begin
    with Client do
    begin
      Connect;
      try
        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.open;
        IOHandlerStack.Write(iSize, True);       ' theoretically this puts it into network byte order and writes the 4 byte header
        IOHandlerStack.Write(sXML);
        s := IOHandlerStack.AllData;
        IOHandlerStack.Close;
      finally
        Disconnect;
      end;
    end;
    iLong := Length(s);
    ShowMessage('Returned ' + IntToStr(iLong) + ' bytes. Here they are:' + Chr(13) + Chr(10) + s);
end;


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)
        ClientSocket.Send(BytesToSend)

        '-- 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
            Else
                NumBytesToRead = ResponseBuffer.Length
            End If

        End While

        '-- Shutdown and Close the socket
        ClientSocket.Shutdown(SocketShutdown.Both)
        ClientSocket.Close()

        Return ResponseString
    End Function
End Class
bjones8888President

Author

Commented:
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.
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.