Strange UDP Socket problem

T

Terry

It's my understanding of UDP sockets that if there is a thread blocked on a
"recvFrom()" call and other thread sends a UDP packet to some address, that
if the machine on the other end isn't up, that the "recvFrom()" call should
just continue blocking. Right?

What I'm seeing is that when I send a packet to a particular address that is
not responding, my "recvFrom()" call throws an exception. I get "An
existing connection was forcibly closed by the remote host". It shouldn't
do that, right? UDP is connectionless, so if the machine on the other end
isn't running (well, I assume it's not running) it should just continue
blocking right?

Here's the basics of the code. I create the socket.
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
_socket.Bind(new IPEndPoint(IPAddress.Any, 1400) );

Then I spawn two threads, one for reading, one for sending:

_readThread = new Thread(new ThreadStart(read) );
_readThread.Start();

_sendThread = new Thread(new ThreadStart(send) );
_sendThread.Start();

The read thread just blocks on a "recvFrom()" until it gets data (or in this
case throws the above exception). The sending thread has a list of IPs it
needs to send requests to. It may or may not get a response to the request.

Why would I be getting a "connection closed" exception on a UDP socket?
What am I missing in my understanding of how UDP works?



Terry
 
W

William Stacey

I suspect you would get the same response if you used one thread to send and
receive. That IP likely does exist and closed the connect, you justed
picked up the exception on the other thread because your sharing the socket
(at least it looks that way.) Even if you can't ping the ip, it could still
exist, just ICMP is being blocked. Try it to a local private ip that you
know for sure does not exist. You may just want to send and receive in same
thread on same socket. Add another thread or two (each doing send/receive)
on ephemeral ports if your looking to get more sends off while waiting for
receives.
 
T

Terry

I guess I was just expecting different behavior. I figured that if I send a
UDP packet to an IP that doesn't have anything at the other end that my
"recvFrom()" call would just block forever waiting for something to come
back.

Hmm. Maybe I can't do what I'm trying to do. I have a list of IP's that I
need to send an identical packet to and time the round-trip time. I thought
I could just have one thread just cycling through the IPs sending the
datagram, and having another thread to just read the responses as they come
in.

So, are you saying that I won't be able to do another "sendTo()" until the
response has been received or I get a WSAECONNRESET indicating there's
nothing at the other end?
 
W

William Stacey

I guess I was just expecting different behavior. I figured that if I send
a
UDP packet to an IP that doesn't have anything at the other end that my
"recvFrom()" call would just block forever waiting for something to come
back.

It should unless you set the receive timeout (which I would do). I am
guessing that a host by that IP does, infact, exist and it closed the
connect. Do you get the same behavior using an IP you know for sure does
not exist? If you don't set a timeout, it will wait forever unless it gets
closed (like yours) or get a reply.
Hmm. Maybe I can't do what I'm trying to do. I have a list of IP's that I
need to send an identical packet to and time the round-trip time.

Sounds like ping? You have a udp server program listening on the other
side?
I could just have one thread just cycling through the IPs sending the
datagram, and having another thread to just read the responses as they come
in.

AFAICT, that should work ok if your sharing the same UdpClient or socket.
As long as one thread does nothing but post sends and the other posts reads.
Again, not sure if socket exceptions will be thrown on the right thread or
in some random fashion. Not sure if this helps, buy I got this reply from a
ms poster awhile back:

"However for some of the user scenarios the UdpClient class can be used
without explicit synchronization.
Such as one thread could be doing read and other thread could be doing
send, fine.
Concurrent Reads are not supported on UdpClient though they are supported
on the socket level.
Concurrent Sends are supported as far as the remote address does not
change."

You could also show the code your using and I could test a few things.

hth
 
W

William Stacey

I did some testing on this and its does not have to do with the threads.
You get the same error sending and receiving to an IP that exists, but is
not listening on that port. If the IP does not exist then it blocks forever
or until timeout. Guess that makes sense. You don't get an error until you
actually try to read something. You can catch that exception and move on.
You know the IP is alive, but nothing is listening on that port or is forced
down. If you could not reach the IP at all, it would block waiting as you
never get any udp reply back to post an exception.
HTH
 
T

Terry

Well, for what it's worth, here's the code boiled down to a small console
example. I grabbed 9 IPs from my set and added a bogus IP for the 10th.
I'm not sure what was going on yesterday, but this works great (as
expected). I didn't realize that a WSAECONNRESET error was expected on a
udp socket if the other end is up, but not open on the specified port.

Here's the code - maybe it will be useful to someone else. I suppose you
could use it as a Half-Life server pinger. :)

using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace TestUdpSendRecv
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
private static Socket _socket = null;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
byte[] buffer = {0xFF, 0xFF, 0xFF, 0xFF,
(byte)'p', (byte)'i', (byte)'n', (byte)'g', 0x00};

IPEndPoint[] eps = {
new IPEndPoint(IPAddress.Parse("194.70.27.10"),27016),
new IPEndPoint(IPAddress.Parse("213.139.175.131"),27025),
new IPEndPoint(IPAddress.Parse("64.156.2.132"),27019),
new IPEndPoint(IPAddress.Parse("81.2.166.133"),27015),
new IPEndPoint(IPAddress.Parse("66.208.100.134"),27015),
new IPEndPoint(IPAddress.Parse("62.73.33.10"),27015),
new IPEndPoint(IPAddress.Parse("62.73.33.10"),27015),
new IPEndPoint(IPAddress.Parse("208.187.58.4"),27015),
new IPEndPoint(IPAddress.Parse("80.55.110.10"),27015), // Throws
exception
new IPEndPoint(IPAddress.Parse("10.0.0.199"),27015) // Invalid IP
};

_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
// Timeout after 3 seconds
_socket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 3000);

// Create the receiving thread
Thread recvThread = new Thread(new ThreadStart(recv) );
recvThread.Name = "Receive Thread";
recvThread.Start();

int nBytesSent = 0;
for (int i=0; i<eps.Length; i++ )
{
Console.WriteLine("Sending to {0}", eps);
nBytesSent = _socket.SendTo(buffer, eps);
//Thread.Sleep(10); // Testing - try throttling
}

recvThread.Join();
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();

Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}

private static void recv()
{
// Sleep for a bit - get "invalid option exception" if ReceiveFrom is
called before a send
Thread.Sleep(50);

byte[] buff = new byte[8192];
int nBytes = 1; // Init to '1' just to get into while loop
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0 );
EndPoint ep = (EndPoint)iep;

try
{
while (nBytes > 0)
{
try
{
nBytes = _socket.ReceiveFrom(buff, ref ep);
Console.WriteLine("{0} bytes from {1}", nBytes, ep);
}
catch (SocketException ex)
{
if (ex.ErrorCode != 10054 )
{
// Break out of not an 'expected' exception (WSAECONNRESET)
Console.WriteLine(ex.Message);
break;
}

Console.WriteLine("Caught exception \"{0}\"", ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top