C
C#7mo ago
Asher

UdpClient doesn't receive data with ReceiveAsync but does with BeginReceive

Hello all, I'm making a torrent client and I'm facing a problem
4 Replies
Asher
Asher7mo ago
I have 2 versions of a ExecuteUDPRequest method, 1 using BeginReceive to receive and one using ReceiveAsync, the ReceiveAsync hangs, it doesn't crash or anything, just hangs. BeginReceive:
private async Task<byte[]?> ExecuteUdpRequest(Uri uri, byte[] message)
{
if (uri == null) throw new ArgumentNullException(nameof(uri));
if (message == null) throw new ArgumentNullException(nameof(message));

byte[]? data = null;
IPEndPoint any = new(IPAddress.Any, ListenPort);

try
{
using (UdpClient udpClient = new UdpClient())
{
udpClient.Client.SendTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
udpClient.Client.ReceiveTimeout = (int)TimeSpan.FromSeconds(15).TotalMilliseconds;

Logger.Log($"sending message to {uri.Host}, returning.", source: "UdpRequest");

int numBytesSent = await udpClient.SendAsync(message, message.Length, uri.Host, uri.Port);
Logger.Log($"Sent: {numBytesSent}", source: "UdpRequest");

var res = udpClient.BeginReceive(null, null);
//data = udpClient.EndReceive(res, ref any);
// begin recieve right after request
if (res.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
Logger.Log($"Recieved message from endpoint, returning.", source: "UdpRequest");

#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
data = udpClient.EndReceive(res, ref any);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
else
{
Logger.Log($"No Bytes Recieved from UdpRequest", source: "UdpRequest");
// here the client just times out.
}
}
}
catch(SocketException ex)
{
Logger.Log($"Failed UDP tracker message to {uri} for torrent {Torrent.InfoHash}: {ex.Message}");
}

return data;
}
}
private async Task<byte[]?> ExecuteUdpRequest(Uri uri, byte[] message)
{
if (uri == null) throw new ArgumentNullException(nameof(uri));
if (message == null) throw new ArgumentNullException(nameof(message));

byte[]? data = null;
IPEndPoint any = new(IPAddress.Any, ListenPort);

try
{
using (UdpClient udpClient = new UdpClient())
{
udpClient.Client.SendTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
udpClient.Client.ReceiveTimeout = (int)TimeSpan.FromSeconds(15).TotalMilliseconds;

Logger.Log($"sending message to {uri.Host}, returning.", source: "UdpRequest");

int numBytesSent = await udpClient.SendAsync(message, message.Length, uri.Host, uri.Port);
Logger.Log($"Sent: {numBytesSent}", source: "UdpRequest");

var res = udpClient.BeginReceive(null, null);
//data = udpClient.EndReceive(res, ref any);
// begin recieve right after request
if (res.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
Logger.Log($"Recieved message from endpoint, returning.", source: "UdpRequest");

#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
data = udpClient.EndReceive(res, ref any);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
else
{
Logger.Log($"No Bytes Recieved from UdpRequest", source: "UdpRequest");
// here the client just times out.
}
}
}
catch(SocketException ex)
{
Logger.Log($"Failed UDP tracker message to {uri} for torrent {Torrent.InfoHash}: {ex.Message}");
}

return data;
}
}
ReceiveAsync:
private async Task<byte[]?> ExecuteUdpRequest2(Uri uri, byte[] message)
{
byte[]? data = null;
if (uri == null) throw new ArgumentNullException(nameof(uri));
if (message == null) throw new ArgumentNullException(nameof(message));

int port = await Torrent.PortList.AwaitOpenPort();

IPEndPoint ep = new IPEndPoint(IPAddress.Any, port);

try
{
using UdpClient udpClient = new(ep);

Logger.Log($"Listening on : {ep}");

udpClient.Client.SendTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
udpClient.Client.ReceiveTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;

Logger.Log($"sending message to {uri.Host}, returning.", source: "UdpRequest");

int numBytesSent = await udpClient.SendAsync(message, message.Length, uri.Host, uri.Port);
Logger.Log($"Sent: {numBytesSent}", source: "UdpRequest");

UdpReceiveResult? res;
//var res = udpClient.BeginReceive(null, null);
try
{
CancellationTokenSource cts = new CancellationTokenSource();

var timer = new Timer(state => cts.Cancel(), null, 5000, Timeout.Infinite);
res = await udpClient.ReceiveAsync(cts.Token);
}
catch (OperationCanceledException ex)
{
res = null;
}
// begin recieve right after request
if (res != null && res?.Buffer != null && res?.Buffer.Length > 0)
{
Logger.Log($"Recieved message from endpoint, returning.", source: "UdpRequest");

data = res?.Buffer;
}
else
{
Logger.Log($"No Bytes Recieved from UdpRequest", source: "UdpRequest");
// here the client just times out.
}
}
catch (SocketException ex)
{
Logger.Log($"Failed UDP tracker message to {uri} for torrent {Torrent.InfoHash}: {ex.Message}");
}
Torrent.PortList.SetPortUnused(port);
return data;
}
private async Task<byte[]?> ExecuteUdpRequest2(Uri uri, byte[] message)
{
byte[]? data = null;
if (uri == null) throw new ArgumentNullException(nameof(uri));
if (message == null) throw new ArgumentNullException(nameof(message));

int port = await Torrent.PortList.AwaitOpenPort();

IPEndPoint ep = new IPEndPoint(IPAddress.Any, port);

try
{
using UdpClient udpClient = new(ep);

Logger.Log($"Listening on : {ep}");

udpClient.Client.SendTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
udpClient.Client.ReceiveTimeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;

Logger.Log($"sending message to {uri.Host}, returning.", source: "UdpRequest");

int numBytesSent = await udpClient.SendAsync(message, message.Length, uri.Host, uri.Port);
Logger.Log($"Sent: {numBytesSent}", source: "UdpRequest");

UdpReceiveResult? res;
//var res = udpClient.BeginReceive(null, null);
try
{
CancellationTokenSource cts = new CancellationTokenSource();

var timer = new Timer(state => cts.Cancel(), null, 5000, Timeout.Infinite);
res = await udpClient.ReceiveAsync(cts.Token);
}
catch (OperationCanceledException ex)
{
res = null;
}
// begin recieve right after request
if (res != null && res?.Buffer != null && res?.Buffer.Length > 0)
{
Logger.Log($"Recieved message from endpoint, returning.", source: "UdpRequest");

data = res?.Buffer;
}
else
{
Logger.Log($"No Bytes Recieved from UdpRequest", source: "UdpRequest");
// here the client just times out.
}
}
catch (SocketException ex)
{
Logger.Log($"Failed UDP tracker message to {uri} for torrent {Torrent.InfoHash}: {ex.Message}");
}
Torrent.PortList.SetPortUnused(port);
return data;
}
the weird thing is, ReceiveAsync does not respect my Cancellation Token, it just hangs forever. no exceptions thrown either I find it particularly weird that ReceiveAsync does not take in an endpoint as a parameter like BeginReceive does but there is no overload where it takes such a parameter any knows why this might hang?
WEIRD FLEX
WEIRD FLEX7mo ago
ps you don't need a timer for the cancellationTokenSource you can just pass the interval to it in the constructor iirc also you haven't said if the BeginReceive version calls successfully EndReceive
Asher
Asher7mo ago
It does, basically the one with BeginReceive & EndReceive is my current working version I can't run it multiple times though I might take a look at the .net library source to see exactly how ReceiveAsync works so I might get a clue or try to use Socket instead of UdpClient thats like my other 2 options right now but I can't for the life of me figure out why this hangs
WEIRD FLEX
WEIRD FLEX7mo ago
i don't think that would help if you just do a little local test with the udpclient i think you can sort it out faster