Solved

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

Posted on 2006-06-17
3
1,084 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;
0
Comment
Question by:bjones8888
  • 2
3 Comments
 

Author Comment

by:bjones8888
Comment Utility
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
0
 

Author Comment

by:bjones8888
Comment Utility
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.
0
 
LVL 1

Accepted Solution

by:
kodiakbear earned 0 total points
Comment Utility
Closed, 500 points refunded.
kb
Experts Exchange Moderator
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

771 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now