4

I'm trying to use the TUdpSocket in Delphi. What I want to do is connect to a UDP server, send some data and wait for answer. Data is sent correctly, but the control does not receive anything. I don't know why. I've been struggling with this problem for many hours now and I'm going to give up :-(.

I tried to use TIdUDPClient, but the situation is the same. Data is sent properly, but none is received.

Only does TIdUDPServer work more or less properly for it both sends and receives data. Unfortunately data reception is handled by a separate thread (main or other, depending on the ThreadedEvent property) which forces me to use synchronization and complicate the whole code. I would like to handle UDP connection in my own thread. Just send some data and call WaitForData() to wait for an answer and then handle it in the same thread.

And if opossible, I don't want to use any third party controls, but if it is the only solution, I accept it.

Thank you very, very much for your help in advance.

---- Examples ---

i) TUDPSocket:

var
  lR, lW, lE: boolean;
begin
  UdpSocket1.LocalPort := '1600';

  UdpSocket1.RemotePort := '1600';
  UdpSocket1.RemoteHost := '127.0.0.1';
  UdpSocket1.Connect;

  UdpSocket1.Sendln('test');

  UdpSocket1.Select(@lR, @lW, @lE, 2000);    
  if lR then
    ShowMessage(UdpSocket1.Receiveln());
end;

As you can see, the control should receive the data it transmits. And apparently it does, for the lR evaluates to true after the Select() method is called. But Receiveln() returns an empty string, as ReceiveBuf() does. When i start a UDP server and send some data to it, it is received properly, so I'm certain that the data is really sent.

Mariusz Schimke
  • 3,185
  • 8
  • 45
  • 63
  • I had a similar thing with ICS in a thread, so cannot answer directly. However, you probably need to include a Message Pump in your thread so that the socket gets the notifications it needs to work. – mj2008 Oct 14 '10 at 10:06
  • 1
    How do you know data is sent correctly? Are both server and client on the same sub-network? are you sure a firewall (local or in-the-net) is not blocking your udp traffic? – jachguate Oct 14 '10 at 23:01
  • What jachguate said: use something like Wireshark to make sure that the server machine actually receives the packet. If it does, and you can see the response hitting the client's NIC, then you might have an issue with socket notifications not being processed. – Frank Shearar Oct 15 '10 at 12:32
  • mj2008: I made all my simple test in the main thread context and as far as I know, it handles a message queue, so I don't have to implement it by myself, right? Therefore I think it's not the source of the problem :-(. Thanks anyway! – Mariusz Schimke Oct 15 '10 at 20:18
  • Jachguate, I made my tests on a single computer, using the 127.0.0.1 address. I just started a UDP server and it received data from my test application. It was also able to respond with some data, but my app never received it unless I used the IdUDPServer which I don't prefer. What do you mean about those notifications not being processed? Can I fix that problem in any way? Is it a bug? Thanks! – Mariusz Schimke Oct 15 '10 at 20:25

2 Answers2

2

You should need nothing more than this:

function SayHi(Host: String; Port: Integer): String;
var
  Client: TIdUDPClient;
begin
  Client := TIdUDPClient.Create(nil);
  try
    Client.Host := Host;
    Client.Port := Port;
    Client.Send('Hello');
    Result := Client.ReceiveString(1000);
  finally
    Client.Free;
  end;
end;

which will, in the same thread, send a UDP packet and either receive a UDP packet or raise an exception after a timeout.

If the above doesn't work, check (using something like Wireshark that the client's actually sending the data. Then check on the server that it's actually receiving the packet.

Send() ultimately (on Windows) calls WinSock's sendto(), and ReceiveString() uses select() and recvfrom(). You could sort've simulate a TIdUDPServer by

while not Terminated do begin
  S := Client.ReceiveString(1000);
  DoSomething(S);
end;
Frank Shearar
  • 17,012
  • 8
  • 67
  • 94
  • Yes, this is exactly what I would like to get. If only it worked ;-(. For it does not. Client.Send() works perfectly. But Cleint.ReceiveString() never does. I don't really understand it. Is it a matter of Indy version? May my version have a bug? Thanks! – Mariusz Schimke Oct 15 '10 at 20:28
  • I'm still using Indy 9, but I can't imagine Indy 10 working much differently. I'll add some more details to the answer. – Frank Shearar Oct 15 '10 at 21:20
  • I'm using Indy 10 (BDS 2006). I don't know why, but it does not work this way :-(. What's more suprising, I also tried using the Select() method of the TUDPSocket. It returns boolean values like ReadReady, WriteReady. When some data was sent to the port I opened, the ReadReady was set to true, but nevertheless no data could be received when I called any of the Receive...() methods. It's an enigma :-). But let's forget about it. I don't want to bother you any more. Thank you very much for your time! – Mariusz Schimke Oct 17 '10 at 08:46
  • BTW, when I run your code, I always get an exception saying: "Socket Error # 10054 / Connection reset by peer" when the ReceiveString() method is invoked. – Mariusz Schimke Oct 17 '10 at 09:13
  • I assume that the host:port to which you send is actually open? `recvfrom()`'s documentation says "On a UDP-datagram socket this error indicates a previous send operation resulted in an ICMP Port Unreachable message." – Frank Shearar Oct 17 '10 at 13:26
0

Another possibility would be to use Synapse for your communication. It is a simple communications library which performs blocking calls, so works well in a single worker thread where you want to stop and wait for data rather than rely on an event to fire. The latest versions in SVN have support for the latest versions of Delphi.

skamradt
  • 15,366
  • 2
  • 36
  • 53