How to build a robust/scalable async await echo server? - c#

Im building a simple TCP client and server as a basis for my networking project. Im planning to use the async await technique for future proof and scaleable server.
If I put wrong ip address, the client cant connect to my server and throw an exception. I can catch the exception using try/catch but is that the recommended way to do?
What do you guys think of the implementation. Any comments for me to improve?
My server
private void startServer_Click(object sender, RoutedEventArgs e)
{
if (anyIP.IsChecked == true)
{
listener = new TcpListener(IPAddress.Any, Int32.Parse(serverPort.Text));
Logger.Info("Ip Address : " + IPAddress.Any + " Port : " + serverPort.Text);
}
else
{
listener = new TcpListener(IPAddress.Parse(serverIP.Text), Int32.Parse(serverPort.Text));
Logger.Info("Ip Address : " + serverIP.Text + " Port : " + serverPort.Text);
}
try
{
listener.Start();
Logger.Info("Listening");
HandleConnectionAsync(listener, cts.Token);
}
//finally
//{
//cts.Cancel();
//listener.Stop();
//Logger.Info("Stop listening");
//}
//cts.Cancel();
}
async Task HandleConnectionAsync(TcpListener listener, CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
Logger.Info("Accepting client");
//TcpClient client = await listener.AcceptTcpClientAsync();
TcpClient client = await listener.AcceptTcpClientAsync();
Logger.Info("Client accepted");
EchoAsync(client, ct);
}
}
async Task EchoAsync(TcpClient client, CancellationToken ct)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
var amountRead = await stream.ReadAsync(buf, 0, buf.Length, ct);
Logger.Info("Receive " + stream.ToString());
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct);
Logger.Info("Echo to client");
}
}
private void stopServer_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
listener.Stop();
Logger.Info("Stop listening");
}
My client
private void connect_Click(object sender, System.Windows.RoutedEventArgs e)
{
IPAddress ipAddress;
int port;
//TODO Check if ip address is valid
ipAddress = IPAddress.Parse(serverIP.Text);
//TODO port range is 0-65000
port = int.Parse(serverPort.Text);
StartClient(ipAddress, port);
}
private static async void StartClient(IPAddress serverIpAddress, int port)
{
var client = new TcpClient();
//can i try/catch to catch await exception?
try
{
await client.ConnectAsync(serverIpAddress, port);
}
catch (Exception e)
{
Logger.Info(e);
}
Logger.Info("Connected to server");
using (var networkStream = client.GetStream())
using (var writer = new StreamWriter(networkStream))
using (var reader = new StreamReader(networkStream))
{
writer.AutoFlush = true;
for (int i = 0; i < 10; i++)
{
Logger.Info("Writing to server");
await writer.WriteLineAsync(DateTime.Now.ToLongDateString());
Logger.Info("Reading from server");
var dataFromServer = await reader.ReadLineAsync();
if (!string.IsNullOrEmpty(dataFromServer))
{
Logger.Info(dataFromServer);
}
}
}
if (client != null)
{
client.Close();
Logger.Info("Connection closed");
}
}

I have a .NET TCP/IP FAQ that I recommend to get some of the basics down.
After just a brief look at your code, these points stood out to me:
Both your client and server have times when they're only reading (not writing). This means you're subject to the half-open scenario (as I describe in my FAQ). A robust server should be writing periodically even if it has nothing to say.
Both your client and server have times when they're only writing (not reading). This means that you're subject to a deadlock (as I describe in my FAQ) if the other end is not behaving well (e.g., sending lots of data). However, you can't just read indefinitely or you'll open yourself up to a DoS; so you should decide where your limit is and establish read buffer sizes (and write timeouts) that make sense for your application.
Using ReadLineAsync leaves you open to a trivial DoS attack, since you can't specify the maximum allowed size of the line.
Your code must be prepared for an exception at any time (as I describe in my FAQ). Obviously, ReadAsync and WriteAsync may throw. What's less obvious is that any socket method may throw, including AcceptTcpClientAsync.
Your code uses a mixture of exception handling types. The async Task methods are never awaited, so exceptions there just silently end that method. The StartClient method is more problematic, since it is async void. You'll need to think through your application needs for error detection and retry strategies, and apply proper handling at every level.
In conclusion, I reiterate my comment: I strongly recommend just self-hosting SignalR. Sockets should only be used if you have no choice.

Related

NetworkStream Async Read -> Cancel

Currently I try to read and write Async to/from a network stream. My software is the Client part and the server can send informations on its own or respond to commands I send him.
So I need a socket which
reads all the time (in case the server sends status informations)
stops reading when I want to send commands (commands can be sequences of data with multible Write and Read operations)
So I thought it would be a good approach to create a Semaphore and a Background Task which handles the server sent messages and in case I want to send a command I block the semaphore and have full access to read/write operations to the socket.
Here is what I do currently.
private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);
public async Task ConnectAsync()
{
if (_tcpClient.Connected)
{
await DisconnectAsync();
}
await _tcpClient.ConnectAsync(Hostname, RemotePort);
//here the background Task is started
_ = AutoReceiveMessages();
}
private async Task AutoReceiveMessages()
{
while (_tcpClient.Connected)
{
//enter and lock semaphore
await ClientSemaphore.WaitAsync();
try
{
//read from socket until timeout (ms)
var msg = await ReadFromSocket(2000);
foreach (var cmd in SplitMessageInTelegrams(msg))
{
Console.WriteLine("MESSAGE --> " + cmd);
}
}
catch (Exception ex)
{
}
finally
{
//release semaphore
ClientSemaphore.Release();
}
}
}
private async Task<string> ReadFromSocket(double timeout = 0)
{
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
//read from stream or timeout
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));
await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
//timeout
if (!amountReadTask.IsCompleted)
{
throw new TimeoutException("Timeout");
}
//no timeout
return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}
But this do not work as I expected...
I use this methode to send a message to the server and in WireShark I see the server resonds with the same message
protected async Task SendTelegramAsync(ITelegram telegram)
{
await ClientSemaphore.WaitAsync();
try
{
_ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");
Console.WriteLine("WRITE --> " + msg);
await stream.WriteAsync(msg, 0, msg.Length);
//comment AutoReceiveMessage and remove comment from this
//and I get responses from the server
//var test = await ReadFromSocket(2000);
}
finally
{
ClientSemaphore.Release();
}
}
I know in this case I do not need the semaphore but later I want to create sequences so one command consists of multible writes and reads and as long as the command is executed I do not want to use the AutoReceiveMessages method.
The problem now is
If I use it like this I never get a response the ReadFromSocket method always get the timeout even when wireshark tell me the server has responded
But even better if I disable AutoReceiveMessages (just comment _ = AutoReceiveMessages()) and use ReadFromSocket directly in SendTelegramAsync() everything work as expected.
So I think the problem is something related to the background task and the ReadAsync but I couldnt figure it out...
Got It!
stream.DataAvailable is your friend (or my friend :)).
If I check before the ReadAsync if DataIsAvailable then I have no problem anymore.
if (_tcpClient.GetStream().DataAvailable)
var msg = await ReadFromSocket(DEFAULT_TIMEOUT);

C# how to use 'await' to wait for connection for an Asynchronous Client Socket [duplicate]

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.

C# encapsulation when getting updates from asynchronous method

I have a tcp connection I want to keep open in the HandleConnectionAsync method of the server class. It will be receiving continuous updates from a client.
private async void HandleConnectionAsync(TcpClient tcpClient)
{
Console.WriteLine("Got connection request from {0}", tcpClient.Client.RemoteEndPoint.ToString());
try
{
using (var networkStream = tcpClient.GetStream())
using (var reader = new StreamReader(networkStream))
using (var writer = new StreamWriter(networkStream))
{
writer.AutoFlush = true;
while (true)
{
string dataFromClient = await reader.ReadLineAsync();
Console.WriteLine(dataFromClient);
await writer.WriteLineAsync("FromServer-" + dataFromClient);
}
}
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
I want to be able to receive the updates the reader puts into dataFromClient without having to put my code in the midst of my server implementation class.
So:
static void Main(string[] args)
{
Server s = new Server();
s.start();
//get dataFromClient as it comes in here
}
Problem is I'm not sure how to do this. Some kind of callback or event?
There are many many ways.
The least code is to pass an Action to your server eg:
Server s = new Server();
s.start(dataFromClient =>
{
// do something with data from client
});
And in HandleConnectionAsync call the action eg:
private async void HandleConnectionAsync(TcpClient tcpClient, Action<string> callback)
{
..
// Console.WriteLine(dataFromClient);
callback(dataFromClient);
..
However this does not address a few things e.g. many clients at the same time and needing to know which client is which.

NetworkStream.GetStream() never returns

I'm really confused. I am able to connect a TCPClient to a tcp server asynchronously. In my callback I now want to start reading some data, but when I go stream = tcpClient.GetStream(); my program doesn't exactly hang, it just does nothing. It won't go to the next line of the method, but the UI is still running (it's Unity, maybe it is multithreaded or something).
public void SetupSocket() {
try {
tcpClient = new TcpClient(host, port);
tcpClient.BeginConnect(host, port, ConnectCallback, tcpClient);
}
catch (Exception e) {
// stuff happens
return;
}
}
private void ConnectCallback(IAsyncResult result) {
if (ConnectedToServer != null)
ConnectedToServer(this, new ServerEventArgs("Connected to server."));
Debug.Log("Where am I?"); // it does get here
try {
stream = tcpClient.GetStream();
}
catch (InvalidOperationException e){
Debug.Log(e); // no exception
}
Debug.Log("Hello?"); // never gets here
BeginReadData();
}
public void BeginReadData() {
Debug.Log(stream.CanRead); // No log here!
if (stream.CanRead) {
stream.BeginRead(tcpStateObject.buffer, 0, tcpStateObject.bufferSize, EndReadData, stream);
}
}
I'm really lost at this point. I can see on my server that I connected, and when I disconnect. I send two messages to the client. It used to work with synchronous sockets, but I wanted async.
Here is the answer
I was connecting twice
tcpClient = new TcpClient(host, port);
tcpClient.BeginConnect(host, port, ConnectCallback, tcpClient);

Accept TCP Client Async

I've been making a server. I am using TcpListener.AcceptTcpClientAsync() in an async method, but I have no idea how to actually make it work. My code right now is:
private static async void StartServer()
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
}
How do I process the client? Do I just continue coding and it will automagically make new threads of the same method or do I need to do some magic method that will do it for me?
Edit: current code:
private static Task HandleClientAsync(TcpClient client)
{
var stream = client.GetStream();
// do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
while (true)
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
await HandleClientAsync(client);
}
}
// all credit should go to c# 7.0 in a nutshell (Joseph Albahari & Ben Albahari)
async void RunServerAsync()
{
var listner = new TcpListener(IPAddress.Any, 9999);
listner.Start();
try
{
while (true)
await Accept(await listner.AcceptTcpClientAsync());
}
finally { listner.Stop(); }
}
const int packet_length = 2; // user defined packet length
async Task Accept(TcpClient client)
{
await Task.Yield();
try
{
using(client)
using(NetworkStream n = client.GetStream())
{
byte[] data = new byte[packet_length];
int bytesRead = 0;
int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
bytesRead += chunkSize =
await n.ReadAsync(data, bytesRead, data.Length - bytesRead);
// get data
string str = Encoding.Default.GetString(data);
Console.WriteLine("[server] received : {0}", str);
// To do
// ...
// send the result to client
string send_str = "server_send_test";
byte[] send_data = Encoding.ASCII.GetBytes(send_str);
await n.WriteAsync(send_data, 0, send_data.Length);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Nothing will magically create dedicated threads for you, although there are some threads used for IO completion which can come into play, particularly if you don't have a synchronization context that you need to return to.
You should decide whether you want your StartServer method to actually complete when it's accepted a single connection, or keep looping until you've been told to shut down.
Either way, you clearly need to decide how to handle the client too. Either you could start a new thread and use synchronous methods, or you could just use asynchronous IO to handle everything in the same thread. For example, to dump the incoming data to a file:
private Task HandleClientAsync(TcpClient client)
{
// Note: this uses a *synchronous* call to create the file; not ideal.
using (var output = File.Create("client.data"))
{
using (var input = client.GetStream())
{
// Could use CopyToAsync... this is just demo code really.
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, bytesRead);
}
}
}
}
(That's assuming the client will just terminate the connection when it's finished writing the data.) Aside from the File.Create call, this is all asynchronous - so there's no need to create a separate thread for it.
This is just an example, of course - real connection handling would usually be more complicated. If your real handling needs anything more compute-intensive, you may well want to consider using Task.Run to use the thread pool... that way it won't interfere with accepting more connections.

Categories