Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Indy TIdTCPClient

Status
Not open for further replies.

vidru

Programmer
Jul 18, 2003
2,113
0
0
US
I have an application that needs to connect via a socket to a server, send an XML request, then receive a response. Sounds pretty simple, right? FWIW, I've never used sockets before.

The vendor of the server has given me sample C#.NET code for connecting/sending/receiving. That's great, and it works great (I've tested it, and it does exactly what it's supposed to do). However, we're using Delphi 5 for this project, and I'm trying to avoid writing a .NET dll that would have to be called from the Delphi app... I'd rather just handle the whole thing in Delphi. We've already had the Indy components for other projects, so I decided I'd give those a go for this.

The problem I'm having is that I can see the XML request I'm sending to the server, but I'm not getting a response back from the server until I've disconnected completely (I can see all of this using Wireshark). When I try to read the response from the server, I get nothing.

The one thing I notice in the .NET socket code is that there is a method that allows you to disconnect the sending portion of the socket. That is what seems to fire the server socket's response. I can't find a method with the Indy components that seems to fit this bill.

Anyone have any suggestions?

Here's my Delphi code:
Code:
with IdTcpClient1 do
  begin
    Connect;
    //CheckServerResponse is the XML string being sent
    WriteBuffer(CHECKSERVERRESPONSE, Length(CHECKSERVERRESPONSE), True);

    Sleep(1000);
    ReadBuffer(Resp, Length(Resp));
    //Resp comes back as an empty string

    Disconnect;
    //At this point, I can see in Wireshark that the server has sent the response I am expecting.

  end;

Here's the working C# code:
Code:
ssSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);

IPAddress hostadd = Dns.Resolve("192.168.40.106").AddressList[0];

IPEndPoint EPhost = new IPEndPoint(hostadd, 2077);

ssSocket.Connect(EPhost);

//variable 'request' constaints the request XML

ssSocket.Send(ASCIIEncoding.ASCII.GetBytes(request));

ssSocket.Shutdown(SocketShutdown.Send);

//Wait for ES Server to send response

int timeOut = 180 * 100000;

if(!ssSocket.Poll(timeOut, SelectMode.SelectRead))

{

throw new Exception("Could not read from SS service. Timed out.");

}

const int iBufferLen = 4096;

byte[] buffer = new byte[iBufferLen];

int iRead = 0;

MemoryStream stResponse = new MemoryStream();

while(0<(iRead = ssSocket.Receive(buffer)))

{

stResponse.Write(buffer, 0, iRead);

}

//Convert response to a plain string xml

string response =
ASCIIEncoding.ASCII.GetString(stResponse.GetBuffer(), 0,
(int)stResponse.Length);

richTextBox1.Text = response;
 
TidTCPClient is a blocking client, meaning that the thread it's in will wait until responses have been sent and received. So get rid of any Sleep commands.

You need to attach your send/receive code to the TIdTCPClient.OnConnect event such as
Code:
[b]procedure[/b] TForm1.IdTCPClient1Connected(Sender: TObject);
[b]var[/b]
  Resp: String;
[b]begin[/b]
  [b]with[/b] IdTCPClient1.IOHandler [b]do[/b]
  [b]begin[/b]
    WriteLn(CHECKSERVERRESPONSE);
    Resp := ReadLn;
  [b]end[/b];
  IdTCPClient1.Disconnect;
[b]end[/b];

This will get called when you call IdTCPClient1.Connect;
 
Thanks, I tried that method earlier as well, but it hangs when it gets to ReadLn.
 
TidTCPClient will wait until the server responds. If it's hanging there it's because the server is not sending anything to your client. It may be the fault of the server, or because your client is not following the communication protocol correctly. This is what you should look at next.
 
Oops - ReadLn will wait until it gets sent a carriage return. If the server is not sending that, then ReadLn will continue to wait.

I missed what you said about WireShark - I presume this is port monitor? If the server is sending the response, then your buffer code must be incorrect. Try using ReadString instead. You'll need to specify how many characters to read.
 
The C# code appears to read from the buffer until it doesn't read anything. This is not good design, because if the server pauses for a moment, it will be interpreted as 'finished' and the client will continue. The server must send some indication of when it's finished transmitting, either by sending a #0, or carriage return or by indicating beforehand how many bytes it's sending.
 
Wireshark is, among other things, a packet sniffer. I have it running listening for traffic on port 2077 (which is the port I have to use to send/receive from the server). So I can see that the server is sending the response, but not until I disconnect the socket.

I've tried ReadString also. In theory, this should work as well, but it hangs on ReadString (again, when it hangs and I reset the project, I can finally see the server try to send the response):
Code:
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
var
  Resp: String;
  SendIt : TStringList;
begin
  SendIt := TStringList.Create();
  SendIt.Add(CHECKSERVERRESPONSE);

  with IdTCPClient1 do
  begin
    WriteStrings(SendIt);
    Resp := ReadString(ReadInteger(True));;
  end;
  IdTCPClient1.Disconnect;
end;
In theory, the above should work, but in practice, it hangs on ReadInteger.
 
It makes no sense for the server to send data after the client disconnects, unless it's part of a 'catch exception' block or something. Has the vendor been able to help you? I can't see how the C# sample code would perform any differently than the Delphi code you have.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top