I've previously used BeginAccept() and BeginRead(), but with Visual Studio 2012 I want to make use of the new asynchronous (async, await) features in my socket server program.
How can I complete the AcceptAsync and ReceiveAsync functions?
using System.Net;
using System.Net.Sockets;
namespace OfficialServer.Core.Server
{
public abstract class CoreServer
{
private const int ListenLength = 500;
private const int ReceiveTimeOut = 30000;
private const int SendTimeOut = 30000;
private readonly Socket _socket;
protected CoreServer(int port, string ip = "0.0.0.0")
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
_socket.Listen(ListenLength);
_socket.ReceiveTimeout = ReceiveTimeOut;
_socket.SendTimeout = SendTimeOut;
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
}
public void Start()
{
}
}
}
...because you're so determined, I put together a very simple example of how to write an echo server to get you on your way. Anything received gets echoed back to the client. The server will stay running for 60s. Try telnetting to it on localhost port 6666. Take time to understand exactly what's going on here.
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6666);
try
{
listener.Start();
//just fire and forget. We break from the "forgotten" async loops
//in AcceptClientsAsync using a CancellationToken from `cts`
AcceptClientsAsync(listener, cts.Token);
Thread.Sleep(60000); //block here to hold open the server
}
finally
{
cts.Cancel();
listener.Stop();
}
}
async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
//once again, just fire and forget, and use the CancellationToken
//to signal to the "forgotten" async invocation.
EchoAsync(client, clientCounter, ct);
}
}
async Task EchoAsync(TcpClient client,
int clientIndex,
CancellationToken ct)
{
Console.WriteLine("New client ({0}) connected", clientIndex);
using (client)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
//under some circumstances, it's not possible to detect
//a client disconnecting if there's no data being sent
//so it's a good idea to give them a timeout to ensure that
//we clean them up.
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
//now we know that the amountTask is complete so
//we can ask for its Result without blocking
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct)
.ConfigureAwait(false);
}
}
Console.WriteLine("Client ({0}) disconnected", clientIndex);
}
You can use TaskFactory.FromAsync to wrap up Begin / End pairs into async-ready operations.
Stephen Toub has an awaitable Socket on his blog which wraps the more efficient *Async endpoints. I recommend combining this with TPL Dataflow to create a fully async-compatible Socket component.
Related
The data I am using arrives by UDP. I want to create several UDP connections without blocking the main thread. I have implemented UDP connections synchronously and asynchronously, however they both keep the main thread locked. My code never reaches 'Console.WriteLine("Past the Async")'
The goal is to have the UDP connections running in the background.
Can anyone provide some direction as to what to try next, or how to properly implement an async version of UDP that allows the main thread to still receive commands?
I comment out ReceiveUdpData() for the async version.
class Program
{
// The whole point of this is to not block the main thread.
static async Task Main(string[] args)
{
ReceiveUdpData();
Console.WriteLine("Past Receive UDP Data");
await foreach (var item in MessagesAsync())
{
Console.WriteLine(item);
}
Console.WriteLine("Past The Async");
}
// Synchronous connection to UDP.
public static void ReceiveUdpData()
{
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
var count = 0;
using (UdpClient client = new UdpClient(12345))
{
while (true)
{
Byte[] receiveBytes = client.Receive(ref remoteEndPoint);
count++;
Console.WriteLine(count);
}
}
}
// Async UDP connection
private static async IAsyncEnumerable<object> MessagesAsync()
{
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
var count = 0;
using (UdpClient client = new UdpClient(12345))
{
while (true)
{
UdpReceiveResult test = await client.ReceiveAsync();
count++;
yield return test.ToString();
}
}
}
}
await will "block" the current method by scheduling the rest of the method to run after the given task continues. It's somewhat similar (not semantically identical to Task.ContinueWith) so csharp Console.WriteLine("Past The Async") will execute only after all the messages are received.
You can not wait for the messages by enumerating the IAsyncEnumerable on a separate task in a "fire and forget" manner:
static async Task Main(string[] args)
{
PrintMessages();
Console.WriteLine("Past The Async");
}
public static async Task PrintMessages()
{
await foreach (var item in MessagesAsync())
{
Console.WriteLine(item);
}
}
But if you do that, your program will terminate as soon as Main exits, so you might actually want to wait, or maybe you have some other work you can perform.
this a basis of program i am using to use an async udp:
sample:
using System;
using System.Net.Sockets;
using System.Net;
class Program {
static void OnUpdateData(IAsyncResult result) {
UdpClient socket = result.AsyncState as UdpClient;
IPEndPoint source = new IPEndPoint(0, 0);
byte[] message = socket.EndReceive(result, ref source);
Console.WriteLine($"Received {message.Length} bytes from {source}");
// schedule the next receive operation once reading is done:
socket.BeginReceive(new AsyncCallback(OnUpdateData), socket);
}
static void Main(string[] args) {
UdpClient socket = new UdpClient(12345);
socket.BeginReceive(new AsyncCallback(OnUpdateData), socket);
//send message with same program or use another program
IPEndPoint target = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
// send a couple of sample messages:
for (int num = 1; num <= 3; num++) {
byte[] message = new byte[num];
socket.Send(message, message.Length, target);
}
Console.ReadKey();
}
}
I have a basic TcpListener server that accepts TcpClient requests and sticks them in separate threads.
The server thread that accepts requests uses await _server.AcceptTcpClientAsync() so it returns cpu resources and doesn't use any cpu until it gets a client request.
My client loop calls await client.GetStream().ReadAsync but that's wrapped in a if (client.Connected && client.Available > 0) so if there is no data it just returns null, so nothing is being awaited.
The options I know of are to add a Thread.Sleep which is bad practice and will slow down my apps, use a EventWaitHandle but I'd have to know set that somehow or to remove the if (client.Connected && client.Available > 0) check, which would sit indefinitely waiting for data, so if the server went down the program would effectively be dead.
EDIT: Added client code
public class TcpServer
{
private TcpListener _server;
private bool _isRunning;
private object _lock = new object();
// Listener thread
public async void StartServer()
{
_server = new TcpListener("127.0.0.1", 1234);
_server.Start();
_isRunning = true;
Thread.CurrentThread.Name = "TcpServer";
while (_isRunning)
{
// Block until client connection
TcpClient newClient = await _server.AcceptTcpClientAsync();
// Create a thread to handle client connection
new Thread(new ParameterizedThreadStart(HandleClientConnection)).Start(newClient);
}
}
// Client Thread
public async void HandleClientConnection(object obj)
{
// Create worker object
var client = (TcpClient)obj;
{
ClientId = Guid.NewGuid()
};
// Read client data
while (client.Connected)
{
var msg = await client.ReadMessageAsync();
}
// Disconnected
}
}
// Read data from stream when available. Used on server and client
public static async Task<Message> ReadMessageAsync(this TcpClient client)
{
if (client.Connected && client.Available > 0)
{
int totalBytesRead = 0;
var data = new byte[client.Available];
while (totalBytesRead < client.Available)
{
totalBytesRead += await client.GetStream().ReadAsync(data, 0, client.Available);
}
return BinarySerializer.DeserializeMessage(data) as Message;
}
return null;
}
// Client Code
public async Task Start()
{
ConnectToServer();
while (_isRunning)
{
// Reconnect to server if needed.
if (!_tcpClient.Connected)
{
ConnectToServer();
}
// Wait for messages
var message = await _tcpClient.ReadMessageAsync();
HandleMessage(message);
}
}
I'm writing a TcpListener service that runs as a Windows Service that a vendor software client will connect to. I can't change the client's implementation and must adhere to their spec.
I have the part that receives messages and returns a response working. The problem I have is the clients expect a heartbeat message (S0000E) to be sent every 5 seconds, to which it replies with the same message. I'm not sure how to add that functionality in among the async/await stuff I'm doing to handle the real messages received from the clients.
OnStart
_serverListenerTask = Task.Run(() => AcceptClientsAsync(_listener, _cancellationToken.Token));
AcceptClientsAsync
static async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
await ReceiveMessageAsync(client, clientCounter, ct);
}
}
ReceiveMessageAsync
static async Task ReceiveMessageAsync(TcpClient client, int clientIndex, CancellationToken ct)
{
Log.Info("New client ({0}) connected", clientIndex);
using (client)
{
var buffer = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buffer, 0, buffer.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
var bytesRead = amountReadTask.Result;
if (bytesRead == 0)
{
// Nothing was read
break;
}
// Snip... Handle message from buffer here
await stream.WriteAsync(responseBuffer, 0, responseBuffer.Length, ct)
.ConfigureAwait(false);
}
}
Log.Info("Client ({0}) disconnected", clientIndex);
}
I thought I could add a heartbeat task to the Task.WhenAny, but that caused the heartbeat to always fire and I could never read the response. I also tried sending the heartbeat before the timeout and read tasks, which worked for sending, but then I was either reading the heartbeat response and not the next message in line or the timeout task would complete and disconnect the client. Essentially, if the heartbeat exchange is successful then the client shouldn't be disconnected after that 15 second delay.
Implementing TCP server-client is not a simple task. However, the following way of implementation, if you improve it to be more efficient in resources, can be a practical solution:
Server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Server
{
public class Program
{
static List<SocketBuffer> clients = new List<SocketBuffer>();
public static void Main(string[] args)
{
//Receive from any IP, listen on port 65000 in this machine
var listener = new TcpListener(IPAddress.Any, 65000);
var t = Task.Run(() =>
{
while (true)
{
listener.Start();
var task = listener.AcceptTcpClientAsync();
task.Wait();
clients.Add(new SocketBuffer(task.Result, new byte[4096]));
}
});
t.Wait(); //It will remain here, do in a better way if you like !
}
}
/// <summary>
/// We need this class because each TcpClient will have its own buffer
/// </summary>
class SocketBuffer
{
public SocketBuffer(TcpClient client, byte[] buffer)
{
this.client = client;
stream = client.GetStream();
this.buffer = buffer;
receiveData(null);
}
private TcpClient client;
private NetworkStream stream;
private byte[] buffer;
private object _lock = new object();
private async void receiveData(Task<int> result)
{
if (result != null)
{
lock (_lock)
{
int numberOfBytesRead = result.Result;
//If no data read, it means we are here to be notified that the tcp client has been disconnected
if (numberOfBytesRead == 0)
{
onDisconnected();
return;
}
//We need a part of this array, you can do it in more efficient way if you like
var segmentedArr = new ArraySegment<byte>(buffer, 0, numberOfBytesRead).ToArray();
OnDataReceived(segmentedArr);
}
}
var task = stream.ReadAsync(buffer, 0, buffer.Length);
//This is not recursion in any sense because the current
//thread will be free and the call to receiveData will be from a new thread
await task.ContinueWith(receiveData);
}
private void onDisconnected()
{
//Add your code here if you want this event
}
private void OnDataReceived(byte[] dat)
{
//Do anything with the data, you can reply here. I will just pring the received data from the demo client
string receivedTxt = Encoding.ASCII.GetString(dat);
Console.WriteLine(receivedTxt);
}
}
}
Demo client:
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Client
{
public class Program
{
public static void Main(string[] args)
{
TcpClient client = new TcpClient();
var task = client.ConnectAsync("localhost", 65000);
task.Wait();
if(client.Connected)
{
Console.WriteLine("Client connected");
var stream = client.GetStream();
var data = Encoding.ASCII.GetBytes("test");
stream.Write(data, 0, data.Length);
}
else
{
Console.WriteLine("Client NOT connected");
}
Thread.Sleep(60000);
}
}
}
I have to connect, send and receive socket messages to a server. I have to implement the client application which is based on Windows Forms. (Target Framework: 4.5.1)
In five minute intervals I have to send a message to the server to keep the server online. (This is not a keep alive ping, this is a specified command to the server.)
I'd like to implement this task to keep the GUI live (not blocked), until the socket communication working in the background.
Everything is working fine, until one or more of the server are not available. The ConnectTaskAsync method wait (approximately 15 seconds) and send a "TIME OUT EXCEPTION". But this time to exception raised is a long time.
How is possible to call connect socket with 5 seconds timeout without blocking UI thread?
On Form load event, I create a timer
private void Form1_Load(object sender, EventArgs e)
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 1000;
t.Tick += this.displayTimers;
t.Start();
System.Windows.Forms.Timer tKeepAlive = new System.Windows.Forms.Timer();
tKeepAlive.Interval = 10000;
tKeepAlive.Tick += clsKeepAlive.keepAlivePanelTimer;
tKeepAlive.Start();
}
keepAlivePanelTimer is caller by timer
public async static void keepAlivePanelTimer(object sender, EventArgs e)
{
List<clsPanelItem> panelList = (List<clsPanelItem>)((clsVmsGroup)actGroup).lstVmsItems;
IEnumerator _idePanel = panelList.GetEnumerator();
while (_idePanel.MoveNext())
{
clsPanelItem actPanel = (clsPanelItem)_idePanel.Current;
try
{
//CancellationTokenSource cts = new CancellationTokenSource();
await keepAlivePanel(actPanel);
}
catch (Exception _ex)
{
//Timeout Exceptions are handle here
System.Diagnostics.Debug.Print("Err: " + _ex.Message + " - Panel: " + actPanel.ipAddress.ToString());
}
finally
{
}
}
}
keepAlivePanel Async task called from timer
private static async Task keepAlivePanel(clsPanelItem actPanel, CancellationToken ct)
{
IPAddress ipAddress = actPanel.ipAddress;
IPEndPoint remoteEP = new IPEndPoint(ipAddress, app.vmsPanelCommPort);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.SendTimeout = 1000; //Nothing was happened
client.ReceiveTimeout = 1000; //Nothing was happened
await async45.ConnectTaskAsync(client, remoteEP, ipAddress, app.vmsPanelCommPort, ct);
}
ConnectTaskAsync connect to server
public static class async45
{
public static Task ConnectTaskAsync(Socket socket, EndPoint endpoint, IPAddress ip, int port, CancellationToken ct)
{
Task t = Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null);
return t;
}
}
Another methods to use send and receive bytes
public static Task SendTaskAsync(this Socket socket, byte[] buffer)
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndSend);
}
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
I tried different things with less success.
// var timeoutTask = Task.Delay(TimeSpan.FromSeconds(2));
// //var ConnectTask = Task.Factory.FromAsync(client.BeginConnect, client.EndConnect, remoteEP, null, TaskCreationOptions.LongRunning);
// var ConnectTask = Task.Factory.FromAsync(client.BeginConnect, client.EndConnect, remoteEP, null);
// var completedTask = await Task.WhenAny(timeoutTask, ConnectTask)
// .ConfigureAwait(false);
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);