quite new to c# and tasks... Trying to find the correct syntax to wrap UDPclient into FromAsync(...), seems I miss some parameters...(can't use ReceiveAsync as it should work with
.NET 4.0)
public Task<byte[]> GetUDPmessageAsync()
{
byte[] data = new byte[100];
myUdpClient = new UdpClient(12000);
Task<byte[]> task = Task<byte[]>.Factory.FromAsync(myUdpClient.BeginReceive, myUdpClient.EndReceive,?,?);
}
I don't think you can use FromAsync since UdpClient.EndReceive take a ref parameter, which makes it incompatible with all the overloads of FromAsync. You can make your own extension method though:
public static Task<byte[]> ReceiveAsync(this UdpClient client, IPEndPoint endpoint)
{
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
AsyncCallback callback = ar => {
try
{
byte[] bytes = client.EndReceive(ar, ref endpoint);
tcs.SetResult(bytes);
}
catch(Exception ex)
{
tcs.SetException(ex);
}
};
client.BeginReceive(callback, null);
return tcs.Task;
}
Related
I am using .NET framework to create a server, which listens on two ports on localhost. It is a simple console application.
It works when I keep connecting to one of the ports, but after I connect to first, another does not respond. First still is alive.
Here is my code:
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
foreach (TcpListener listener in listeners)
{
listener.Start();
}
try
{
while (true)
{
Socket socket = AcceptAnyConnection(listeners).Result;
NetworkStream stream = new NetworkStream(socket);
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
stream.Write(bytes, 0, bytes.Length);
//stream.Close();
socket.Close();
}
}
finally
{
foreach (TcpListener listener in listeners)
{
listener.Stop();
}
}
}
private static async Task<Socket> AcceptAnyConnection(List<TcpListener> listeners)
{
List<Task<Socket>> tasks = new List<Task<Socket>>();
foreach (TcpListener listener in listeners)
{
tasks.Add(AcceptConnection(listener));
}
Task<Socket> completedTask = await Task.WhenAny(tasks);
return await completedTask;
}
private static async Task<Socket> AcceptConnection(TcpListener listener)
{
Socket socket = await listener.AcceptSocketAsync();
return socket;
}
await Task.WhenAny() blocks if I connect to another port.
I must be doing something wrong, but I am not sure what.
BTW, I did try the same with .NET Core console application, and it works fine.
Thanks
I'd suggest refactoring your code to something like this, and taking it from there. That is, run an infinite loop for each listener. That avoids problems with making sure that you only call AcceptTcpClientAsync once at a time for each client.
(Note there's no code to actually stop the listeners. Also completely untested - please use it only as an indication of the sort of approach to take)
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
var listenerTasks = listeners.Select(x => RunTcpListener(x)).ToArray();
Task.WaitAll(listenerTasks);
}
private static async Task RunTcpListener(TcpListener listener)
{
listener.Start();
try
{
while (true)
{
using (var client = await listener.AcceptTcpClientAsync())
{
var stream = client.GetStream();
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
stream.Write(bytes, 0, bytes.Length);
client.Close();
}
}
}
finally
{
listener.Stop();
}
}
Here is my code, after I followed a suggestion by canton7 to move the task list outside of loop and move add and remove tasks "as we go". Probably, I will have to look at it to clean it up, but the idea works:
static void Main(string[] args)
{
IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
List<TcpListener> listeners = new List<TcpListener>()
{
new TcpListener(hostIP, 6060),
new TcpListener(hostIP, 6061)
};
ProcessConnections(listeners).Wait();
}
private async static Task SendData(Socket socket)
{
NetworkStream stream = new NetworkStream(socket);
byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
await stream.WriteAsync(bytes, 0, bytes.Length);
socket.Close();
}
private static async Task ProcessConnections(List<TcpListener> listeners)
{
foreach (TcpListener listener in listeners)
{
listener.Start();
}
try
{
List<Task<Socket>> tasks = new List<Task<Socket>>();
foreach (TcpListener listener in listeners)
{
tasks.Add(AcceptConnection(listener));
}
while (true)
{
int i = Task.WaitAny(tasks.ToArray());
Socket socket = await tasks[i];
await SendData(socket);
tasks.RemoveAt(i);
tasks.Insert(i, AcceptConnection(listeners[i]));
}
}
finally
{
foreach (TcpListener listener in listeners)
{
listener.Stop();
}
}
}
private static async Task<Socket> AcceptConnection(TcpListener listener)
{
Socket socket = await listener.AcceptSocketAsync();
return socket;
}
I think I need async await for my program but I can't figure out how to do it. From GetData method I need send request to socket, and finally get socket data to same method. Need to pass data somehow from OnReceive to initial GetData method. Is it possible to implement await async tasks here? Here is simplified scheme:
AsyncCallback m_pfnLookupCallback;
Socket m_sock;
public void GetData()
{
string data;
if (condition) data = GetDataFromCache();
else data = GetDataFromNet(); /// !NEED AWAIT FOR SOCKET DATA HERE
//will process data here.
}
public string GetDataFromNet()
{
m_sock.Send(szCommand, iBytesToSend, SocketFlags.None);
WaitForData("s1");
}
public void WaitForData(string sSocketName)
{
m_pfnLookupCallback = new AsyncCallback(OnReceive);
m_sock.BeginReceive(m_szLookupSocketBuffer, 0, m_szLookupSocketBuffer.Length, SocketFlags.None, m_pfnLookupCallback, sSocketName);
}
private void OnReceive(IAsyncResult asyn)
{
int iReceivedBytes = 0;
iReceivedBytes = m_sockLookup.EndReceive(asyn);
string sData = Encoding.ASCII.GetString(m_szLookupSocketBuffer, 0, iReceivedBytes); //WHAT I NEED
}
p.s. I would avoid changing socket work if possible because they are used in other parts of program.
You can turn the old IAsyncResult pattern (APM) into async await quite easily. Here's an example of the socket call:
var byteCount = await Task.Factory.FromAsync(
(callback, s) =>
{
return clientSocket.BeginReceive(
m_szLookupSocketBuffer,
0,
cm_szLookupSocketBuffer.Length,
SocketFlags.None,
callback,
sSocketName);
},
result => clientSocket.EndReceive(result),
null);
I have a scenario where multiple threads are sending data over a single socket. A unique ID has been inserted into the message and the unique ID is echoed back in the response message. Everything works great when the socket is isolated to a single client (as expected). Now I'm looking for an async / await pattern for multiple threads where the client waits for a specific response.
Some code to demonstrate:
using (var ns = new NetworkStream(_socket))
{
byte[] requestBuffer = GetBuffer(request);
await ns.WriteAsync(requestBuffer, 0, request.Length);
byte[] responseBuffer = await ReceiveMessageAsync(ns);
// process response message
}
The above example does not work in a multithreaded scenario because messages can come back in any order, so the next message off the wire may not belong to the current client. My thought was that the client would register a delegate or Task using its unique ID (storing that in a Dictionary) and when a message came back with that unique ID, the delegate or task would be 'completed' with the response bytes. I'm guessing this would be fairly easy to implement with an EventHandler but I'm looking for a way to await the response.
For example:
using (var ns = new CustomNetworkStream(_socket))
{
Task waitTask = ns.RegisterTask(this.UniqueId);
byte[] requestBuffer = GetBuffer(request);
await ns.WriteAsync(requestBuffer, 0, request.Length);
byte[] responseBuffer = await waitTask;
// process response message
}
I don't know what the "RegisterTask" method would look like, how to store the task, how to make the task 'wait' and later 'complete' it with Task as the Result.
Any ideas? I've researched Toub's Async Coordination Primitives series but I'm not certain if that's the right approach or not.
So you will want to wrap all of this into a new class, because you're going to need to share state between the places that you read and the places that you write.
Every time you go and write to the stream you'll need to accept the unique ID, and add an entry into a lookup that maps the id to a TaskCompletionSource. The write method can then return the Task from that TCS.
You can then have a separate reader that just sits there reading from your stream, finds the dictionary entry associated with the ID of that response, and sets its result.
public class MyNetworkStream : IDisposable
{
private NetworkStream stream;
private ConcurrentDictionary<int, TaskCompletionSource<byte[]>> lookup =
new ConcurrentDictionary<int, TaskCompletionSource<byte[]>>();
private CancellationTokenSource disposalCTS = new CancellationTokenSource();
public MyNetworkStream(Socket socket)
{
stream = new NetworkStream(socket);
KeepReading();
}
public void Dispose()
{
disposalCTS.Cancel();
stream.Dispose();
}
public Task<byte[]> WriteAndWaitAsync(byte[] buffer, int offset,
int count, int uniqueID)
{
var tcs = lookup.GetOrAdd(uniqueID, new TaskCompletionSource<byte[]>());
stream.WriteAsync(buffer, offset, count);
return tcs.Task;
}
private async void KeepReading()
{
try
{
//TODO figure out what you want for a buffer size so that you can
//read a block of the appropriate size.
byte[] buffer = null;
while (!disposalCTS.IsCancellationRequested)
{
//TODO edit offset and count appropriately
await stream.ReadAsync(buffer, 0, 0, disposalCTS.Token);
int id = GetUniqueIdFromBlock(buffer);
TaskCompletionSource<byte[]> tcs;
if (lookup.TryRemove(id, out tcs))
tcs.TrySetResult(buffer);
else
{
//TODO figure out what to do here
}
}
}
catch (Exception e)
{
foreach (var tcs in lookup.Values)
tcs.TrySetException(e);
Dispose();
//TODO consider any other necessary cleanup
}
}
private int GetUniqueIdFromBlock(byte[] buffer)
{
throw new NotImplementedException();
}
}
What you need is a TaskCompletionSource<byte[]> to use as a synchronization construct, a ConcurrentDictionary to map between an id and a TCS and a listener:
ConcurrentDictionary<UniqueId, TaskCompletionSource<byte[]>> _dictionary;
async Task Listen(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
using (var ns = new NetworkStream(_socket))
{
byte[] responseBuffer = await ReceiveMessageAsync(ns);
var id = ExtractId(responseBuffer);
TaskCompletionSource<byte[]> tcs;
if (dictionary.TryRemove(id, out tcs))
{
tcs.SetResult(responseBuffer);
}
else
{
// error
}
}
}
}
Task RegisterTask(UniqueId id)
{
var tcs = new TaskCompletionSource<byte[]>();
if (!_dictionary.TryAdd(id, tcs))
{
// error
}
return tcs.Task;
}
However, as Stephen Cleary suggested, you probably want to use an existing solution for that.
I've been working on a socket server program in C# (I was inspired from this post) and my problem is that when a client disconnects an exception "An existing connection was forcibly closed by the remote host" appears when the call EndReceiveFrom() and returns 0, the ref clientEP becomes the client normally close. I don't understand why my DoReceiveFrom() function is called if there is nothing to read. I probably missed something. What is wrong ?
Problem appear there :
int dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
The full source code:
class UDPServer
{
private Socket serverSocket = null;
private List<EndPoint> clientList = new List<EndPoint>();
private List<Tuple<EndPoint, byte[]>> dataList = new List<Tuple<EndPoint, byte[]>>();
private byte[] byteData = new byte[1024];
private int port = 4242;
public List<Tuple<EndPoint, byte[]>> DataList
{
private set { this.dataList = value; }
get { return (this.dataList); }
}
public UDPServer(int port)
{
this.port = port;
}
public void Start()
{
this.serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
this.serverSocket.Bind(new IPEndPoint(IPAddress.Any, this.port));
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
}
private void DoReceiveFrom(IAsyncResult iar)
{
try
{
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
byte[] data = new byte[dataLen];
Array.Copy(this.byteData, data, dataLen);
if (!this.clientList.Any(client => client.Equals(clientEP)))
this.clientList.Add(clientEP);
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
DataList.Add(Tuple.Create(clientEP, data));
}
catch (ObjectDisposedException)
{
}
}
public void SendTo(byte[] data, EndPoint clientEP)
{
try
{
this.serverSocket.SendTo(data, clientEP);
}
catch (System.Net.Sockets.SocketException)
{
this.clientList.Remove(clientEP);
}
}
public void SendToAll(byte[] data)
{
foreach (var client in this.clientList)
{
this.SendTo(data, client);
}
}
public void Stop()
{
this.serverSocket.Close();
this.serverSocket = null;
this.dataList.Clear();
this.clientList.Clear();
}
}
Exception:
An existing connection was forcibly closed by the remote host
Update:
I tried to run my client (netcat) on another pc and the exception no longer appears, even when SendTo(), which is also problematic to delete my client in my clientList.
I still do not understand what is happening.
Everything is as it should be.
This is the way all Async methods work: you invoke BeginDo() and pass into it your implementation of AsyncCallback delegate (in your example that's DoReceiveFrom). You implementation starts executing immediately after that - BeginDo() is not a blocking call.
Inside your implementation, you must call EndDo(), which will block until one of two things happen: the object, on which you invoked BeginDo(), actually does something, or it throws an exception doing it. As it does in your case when client disconnects.
The source on the Async method.
What you need to do for the whole thing to work is
Make sure that you handle that client-disconnected exception properly
Make sure that you call BeginReceiveFrom regardless of the way EndReceiveFrom finishes. And preferably, call BeginReceiveFrom immediately after you call EndReceiveFrom. This is needed because while you server is in-between those calls, it does not actually listen to the socket.
I would put another try-catch around EndReceiveFrom.
UPDATE:
private void DoReceiveFrom(IAsyncResult iar)
{
try
{
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int dataLen = 0;
byte[] data = null;
try
{
dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
data = new byte[dataLen];
Array.Copy(this.byteData, data, dataLen);
}
catch(Exception e)
{
}
finally
{
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
}
if (!this.clientList.Any(client => client.Equals(clientEP)))
this.clientList.Add(clientEP);
DataList.Add(Tuple.Create(clientEP, data));
}
catch (ObjectDisposedException)
{
}
}
I have a TcpClient which I use to send data to a listener on a remote computer. The remote computer will sometimes be on and sometimes off. Because of this, the TcpClient will fail to connect often. I want the TcpClient to timeout after one second, so it doesn't take much time when it can't connect to the remote computer. Currently, I use this code for the TcpClient:
try
{
TcpClient client = new TcpClient("remotehost", this.Port);
client.SendTimeout = 1000;
Byte[] data = System.Text.Encoding.Unicode.GetBytes(this.Message);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
data = new Byte[512];
Int32 bytes = stream.Read(data, 0, data.Length);
this.Response = System.Text.Encoding.Unicode.GetString(data, 0, bytes);
stream.Close();
client.Close();
FireSentEvent(); //Notifies of success
}
catch (Exception ex)
{
FireFailedEvent(ex); //Notifies of failure
}
This works well enough for handling the task. It sends it if it can, and catches the exception if it can't connect to the remote computer. However, when it can't connect, it takes ten to fifteen seconds to throw the exception. I need it to time out in around one second? How would I change the time out time?
Starting with .NET 4.5, TcpClient has a cool ConnectAsync method that we can use like this, so it's now pretty easy:
var client = new TcpClient();
if (!client.ConnectAsync("remotehost", remotePort).Wait(1000))
{
// connection failure
}
You would need to use the async BeginConnect method of TcpClient instead of attempting to connect synchronously, which is what the constructor does. Something like this:
var client = new TcpClient();
var result = client.BeginConnect("remotehost", this.Port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!success)
{
throw new Exception("Failed to connect.");
}
// we have connected
client.EndConnect(result);
Another alternative using https://stackoverflow.com/a/25684549/3975786:
var timeOut = TimeSpan.FromSeconds(5);
var cancellationCompletionSource = new TaskCompletionSource<bool>();
try
{
using (var cts = new CancellationTokenSource(timeOut))
{
using (var client = new TcpClient())
{
var task = client.ConnectAsync(hostUri, portNumber);
using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true)))
{
if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
{
throw new OperationCanceledException(cts.Token);
}
}
...
}
}
}
catch(OperationCanceledException)
{
...
}
The answers above don't cover how to cleanly deal with a connection that has timed out. Calling TcpClient.EndConnect, closing a connection that succeeds but after the timeout, and disposing of the TcpClient.
It may be overkill but this works for me.
private class State
{
public TcpClient Client { get; set; }
public bool Success { get; set; }
}
public TcpClient Connect(string hostName, int port, int timeout)
{
var client = new TcpClient();
//when the connection completes before the timeout it will cause a race
//we want EndConnect to always treat the connection as successful if it wins
var state = new State { Client = client, Success = true };
IAsyncResult ar = client.BeginConnect(hostName, port, EndConnect, state);
state.Success = ar.AsyncWaitHandle.WaitOne(timeout, false);
if (!state.Success || !client.Connected)
throw new Exception("Failed to connect.");
return client;
}
void EndConnect(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
TcpClient client = state.Client;
try
{
client.EndConnect(ar);
}
catch { }
if (client.Connected && state.Success)
return;
client.Close();
}
One thing to take note of is that it is possible for the BeginConnect call to fail before the timeout expires. This may happen if you are attempting a local connection. Here's a modified version of Jon's code...
var client = new TcpClient();
var result = client.BeginConnect("remotehost", Port, null, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!client.Connected)
{
throw new Exception("Failed to connect.");
}
// we have connected
client.EndConnect(result);
Here is a code improvement based on mcandal solution.
Added exception catching for any exception generated from the client.ConnectAsync task (e.g: SocketException when server is unreachable)
var timeOut = TimeSpan.FromSeconds(5);
var cancellationCompletionSource = new TaskCompletionSource<bool>();
try
{
using (var cts = new CancellationTokenSource(timeOut))
{
using (var client = new TcpClient())
{
var task = client.ConnectAsync(hostUri, portNumber);
using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true)))
{
if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
{
throw new OperationCanceledException(cts.Token);
}
// throw exception inside 'task' (if any)
if (task.Exception?.InnerException != null)
{
throw task.Exception.InnerException;
}
}
...
}
}
}
catch (OperationCanceledException operationCanceledEx)
{
// connection timeout
...
}
catch (SocketException socketEx)
{
...
}
catch (Exception ex)
{
...
}
As Simon Mourier mentioned, it's possible to use ConnectAsync TcpClient's method with Task in addition and stop operation as soon as possible.
For example:
// ...
client = new TcpClient(); // Initialization of TcpClient
CancellationToken ct = new CancellationToken(); // Required for "*.Task()" method
if (client.ConnectAsync(this.ip, this.port).Wait(1000, ct)) // Connect with timeout of 1 second
{
// ... transfer
if (client != null) {
client.Close(); // Close the connection and dispose a TcpClient object
Console.WriteLine("Success");
ct.ThrowIfCancellationRequested(); // Stop asynchronous operation after successull connection(...and transfer(in needed))
}
}
else
{
Console.WriteLine("Connetion timed out");
}
// ...
Also, I would recommended checking out AsyncTcpClient C# library with some examples provided like Server <> Client.
If using async & await and desire to use a time out without blocking, then an alternative and simpler approach from the answer provide by mcandal is to execute the connect on a background thread and await the result. For example:
Task<bool> t = Task.Run(() => client.ConnectAsync(ipAddr, port).Wait(1000));
await t;
if (!t.Result)
{
Console.WriteLine("Connect timed out");
return; // Set/return an error code or throw here.
}
// Successful Connection - if we get to here.
See the Task.Wait MSDN article for more info and other examples.
I am using these generic methods; they can add timeout and cancellation tokens for any async task. Let me know if you see any problem so I can fix it accordingly.
public static async Task<T> RunTask<T>(Task<T> task, int timeout = 0, CancellationToken cancellationToken = default)
{
await RunTask((Task)task, timeout, cancellationToken);
return await task;
}
public static async Task RunTask(Task task, int timeout = 0, CancellationToken cancellationToken = default)
{
if (timeout == 0) timeout = -1;
var timeoutTask = Task.Delay(timeout, cancellationToken);
await Task.WhenAny(task, timeoutTask);
cancellationToken.ThrowIfCancellationRequested();
if (timeoutTask.IsCompleted)
throw new TimeoutException();
await task;
}
Usage
await RunTask(tcpClient.ConnectAsync("yourhost.com", 443), timeout: 1000);