Description
Description
This might be some underlying platform thing that I'm not understanding. This also requires a little bit of understanding how UDP sockets are supposed to work.
When a UDP packet is sent, the remote host has the possibility of responding with a ICMP port unreachable. No guarantee it will, of course, but the possibility exists. If such a packet has been received in response to a UDP send, it ends up with the next receive socket operation returning an error of some kind. On Windows this is generally ECONNRESET. On Linux this is generally ECONNREFUSED.
.NET sockets, on Windows, dutifully report theses errors up. So, to determine whether you can early-exit an attempt to send a UDP connection, you can trap the SocketException, check for either ConnectionReset. Another thing you can do is use Socket.Poll to wait for a timeout for either new data or a port refusal. So, generally, the following code works in a receive method.
if (socket.Poll(timeout, SelectMode.SelectRead) == false)
throw new global::java.net.SocketTimeoutException("Receive timed out.");
length = socket.ReceiveFrom(packet.buf, packet.offset, packet.bufLength, SocketFlags.None, ref remoteEndpoint);
In the case of a ICMP unreachable, Poll returns immediately with true
, and then the subsequent ReceiveFrom throws.
This however doesn't seem to be working for me on Linux.
What's happening on Linux is that Poll returns immediately with FALSE. No timeout wait. But no way to know that there was no timeout. If I just brute force through:
socket.Poll(timeout, SelectMode.SelectRead)
length = socket.ReceiveFrom(packet.buf, packet.offset, packet.bufLength, SocketFlags.None, ref remoteEndpoint);
Then i properly get ConnectionRefused on ReceiveFrom, with Poll exiting immediately.
Poll DOES wait if no ICMP port unreachable was sent.
It looks like the socket is aware of the ICMP unreachable: it exits immediately, not waiting for the timeout. And it's capable of throwing the exception on the subsequent ReceiveFrom. But the return value of Poll is wrong.
I am trying this on .NET 6.
Reproduction Steps
public static void Main(string[] args)
{
// create a new socket to be a client
var sock = new Socket(SocketType.Dgram, ProtocolType.Udp);
var addr = IPAddress.Loopback;
sock.Connect(addr, 7441);
sock.Send(new byte[] { 0x00 });
// should return true
var r = sock.Poll(1000000, SelectMode.SelectRead);
if (r == false)
throw new Exception();
var b = new byte[1024];
sock.Receive(b, SocketFlags.None);
}
This code should exit by throwing the Connection Refused exception, but does not. It fails with the generic exception.
Expected behavior
Poll should return true
if it exists because of a waiting ICMP packet.
Actual behavior
It returns false.
Regression?
Unknown. It's isolated to Linux on Core.
Known Workarounds
No response
Configuration
No response
Other information
No response