Understanding async behaviour of C# console TCP server - c#

I want to create a simple TCP server in .NET Core 2.0 using asynchrony (because from what I understand, it's more reasonable than spawning threads) with the async/await approach (because I believe it's more up-to-date than the one with IAsyncResult and *Begin/*End methods).
I've written this small server that accepts new connections from clients and then begins to send them 100 messages (with a 1s delay between them).
The main question is:
If I'm not spawning new threads, then how the server continues to send delayed messages to several clients, when in fact it's "waiting for connection"? Are there any hidden low-level signals/events involved, or are there really just new threads?
The second question is:
If I'm not using this brand new async Main syntax sugar and I'm not "awaiting" the async task of sending the messages -- am I using the asynchrony correctly?
class Program
{
public static void Main(string[] args)
{
StartServer();
}
public static void StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = listener.AcceptTcpClient(); // synchronous
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // not awaited -- StartServer is not async
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
await stream.WriteAsync(msg, 0, msg.Length); // returning back to caller?
await Task.Delay(1000); // and what about here?
}
client.Close();
}
}
What is the difference between the original code and the version below? What difference does async Main make?
class Program
{
public static async Task Main(string[] args)
{
await StartServer();
}
public static async Task StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = await listener.AcceptTcpClientAsync(); // does it make any difference when done asynchronously?
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // cannot await here, because it blocks next connections
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
var result = stream.WriteAsync(msg, 0, msg.Length);
await Task.Delay(1000);
await result;
}
client.Close();
}
}

The answer to the main question:
In the background, as a rule, objects work with some api(winapi for windows). These api can implement asynchrony differently. For example, events(winapi events) or callbacks. So the answer is yes - there are hidden signals or threads. For example, you can see the Ping class. Ping.InternalSend using ThreadPool.RegisterWaitForSingleObject for the task of asynchrony.
The answer about async Main:
In your first code because StartServer is not async the Main method will not get back control until your "accept" cycle ends.
In your second code, the Main method will get back control then listener.AcceptTcpClientAsync invoked. But because you using await StartServer(); the Main method will be wait until StartServer ends.
Some code for to explain:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
// not await for exploration
var task = StartServer();
Console.WriteLine("Main StartServer finished");
task.Wait();
Console.WriteLine("task.Wait() finished");
Console.ReadKey();
}
static async Task StartServer()
{
Console.WriteLine("StartServer enter");
for(int i = 0; i < 3; i++) {
await Task.Delay(1000); // listener.AcceptTcpClientAsync simulation
SendHundredMessages();
}
Console.WriteLine("StartServer exit");
}
static async Task SendHundredMessages()
{
Console.WriteLine("SendHundredMessages enter");
await Task.Run(() => {
Thread.Sleep(2000);
});
Console.WriteLine("SendHundredMessages exit");
}
}
}
This code generate this output:
StartServer enter
Main StartServer finished
SendHundredMessages enter
SendHundredMessages enter
SendHundredMessages exit
SendHundredMessages enter
StartServer exit
task.Wait() finished
SendHundredMessages exit
SendHundredMessages exit
As you can see, the Main method continued execution right after first Task.Delay.
A warning:
You do not wait end of SendHundredMessages, and this is very bad. In example output you can see that "SendHundredMessages ending" after "task.Wait() finished". In example application of course it not danger, but in real project you can get big problem.

Related

Running Callback Function in Async

I'm trying to understand how to run callback functions in an async console application. My base console app code looks as follows:
using Nito.AsyncEx;
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync());
}
static async Task MainAsync()
{
}
The method that I want to run in Async mode is the following from a websocket api:
using ExchangeSharp;
public static void Main(string[] args)
{
// create a web socket connection to Binance. Note you can Dispose the socket anytime to shut it down.
// the web socket will handle disconnects and attempt to re-connect automatically.
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}
The above code is meant to lock a console app and subscribe to an event which receives data, and to do something with the received data.
What I want to do is run the above in a separate thread or in async so that I can continue with my code in the MainAsync() function.
My C# experience with this is limited. Will appreciate any help!
According to source code GetTickersWebSocket isn't a blocking call.
The only blocking call you've posted is Console.ReadLine.
ExchangeBinanceAPI has its own callback-based asynchrony, so, just throw away Console.ReadLine, or place more code before it:
static async Task MainAsync()
{
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
// code continues here
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}
As a side note.
I'm not familiar with this project, but source code shows poor man's aynchrony inside WebSocketWrapper:
Task.Factory.StartNew(ListenWorkerThread)
// inside ListenWorkerThread
_ws.ConnectAsync(_uri, CancellationToken.None).GetAwaiter().GetResult();
result = _ws.ReceiveAsync(receiveBuffer, _cancellationToken).GetAwaiter().GetResult();
and so on.
There are attempts to call asynchronous code in synchronous way.
Instead of this, at least ListenWorkerThread must be converted to async method, and it definitely must not be called via Task.Factory.StartNew.
I'd post a request to rewrite code in true asynchronous manner, if I have to use this project.
Async method would not block the caller thread if you use it directly, you can just call it anywhere before Consolo.ReadLine() then use returned Task to handle the result if you need.
public static void Main(string[] args)
{
// Would not block the thread.
Task t = MainAsync();
// Only if you need. Would not block the thread too.
t.ContinueWith(()=> { code block that will run after MainAsync() });
// create a web socket connection to Binance. Note you can Dispose the socket anytime to shut it down.
// the web socket will handle disconnects and attempt to re-connect automatically.
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}

Using 'async' in a console application in C# [duplicate]

This question already has answers here:
Can't specify the 'async' modifier on the 'Main' method of a console app
(20 answers)
Closed 5 years ago.
I have this simple code:
public static async Task<int> SumTwoOperationsAsync()
{
var firstTask = GetOperationOneAsync();
var secondTask = GetOperationTwoAsync();
return await firstTask + await secondTask;
}
private async Task<int> GetOperationOneAsync()
{
await Task.Delay(500); // Just to simulate an operation taking time
return 10;
}
private async Task<int> GetOperationTwoAsync()
{
await Task.Delay(100); // Just to simulate an operation taking time
return 5;
}
Great. This compiles.
But let’s say I have a console application and I want to run the code above (calling SumTwoOperationsAsync()).
static void Main(string[] args)
{
SumTwoOperationsAsync();
}
But I've read that (when using sync) I have to sync all the way up and down:
Does this mean that my Main function should be marked as async?
Well, it can't be because there is a compilation error:
an entry point cannot be marked with the 'async' modifier
If I understand the async stuff , the thread will enter the Main function → SumTwoOperationsAsync → will call both functions and will be out. But until the SumTwoOperationsAsync
What am I missing?
In most project types, your async "up" and "down" will end at an async void event handler or returning a Task to your framework.
However, Console apps do not support this.
You can either just do a Wait on the returned task:
static void Main()
{
MainAsync().Wait();
// or, if you want to avoid exceptions being wrapped into AggregateException:
// MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
...
}
or you can use your own context like the one I wrote:
static void Main()
{
AsyncContext.Run(() => MainAsync());
}
static async Task MainAsync()
{
...
}
More information for async Console apps is on my blog.
Here is the simplest way to do this
static void Main(string[] args)
{
Task t = MainAsync(args);
t.Wait();
}
static async Task MainAsync(string[] args)
{
await ...
}
As a quick and very scoped solution:
Task.Result
Both Task.Result and Task.Wait won't allow to improving scalability when used with I/O, as they will cause the calling thread to stay blocked waiting for the I/O to end.
When you call .Result on an incomplete Task, the thread executing the method has to sit and wait for the task to complete, which blocks the thread from doing any other useful work in the meantime. This negates the benefit of the asynchronous nature of the task.
notasync
My solution. The JSONServer is a class I wrote for running an HttpListener server in a console window.
class Program
{
public static JSONServer srv = null;
static void Main(string[] args)
{
Console.WriteLine("NLPS Core Server");
srv = new JSONServer(100);
srv.Start();
InputLoopProcessor();
while(srv.IsRunning)
{
Thread.Sleep(250);
}
}
private static async Task InputLoopProcessor()
{
string line = "";
Console.WriteLine("Core NLPS Server: Started on port 8080. " + DateTime.Now);
while(line != "quit")
{
Console.Write(": ");
line = Console.ReadLine().ToLower();
Console.WriteLine(line);
if(line == "?" || line == "help")
{
Console.WriteLine("Core NLPS Server Help");
Console.WriteLine(" ? or help: Show this help.");
Console.WriteLine(" quit: Stop the server.");
}
}
srv.Stop();
Console.WriteLine("Core Processor done at " + DateTime.Now);
}
}

Stopping and restarting HttpListener?

I am working on an app that has an HttpListener. My goal is for the user to turn the listener off and on as they choose. I put the Listener in a new thread and I'm having a problem aborting that thread. I read somewhere that if you try to abort a thread that is in an unmanaged context, then as soon as it re-enters a managed context the ThreadAbortException will be fired. It appears that an HttpListener's GetContext() method is unmanaged because when I try to abort the thread nothing happens until I make a web request against my app. THEN the thread exits. The problem is when I attempt to kill the thread, I may start up the thread again later on the same port and an HttpListenerException goes off saying that the prefix is already registered.
How can I kill a cross thread HttpListener? Is there a managed alternative to GetContext() that will allow the thread to abort? Can I abort the thread in a way that unmanaged code will halt?
What about something like:
public class XListener
{
HttpListener listener;
public XListener(string prefix)
{
listener = new HttpListener();
listener.Prefixes.Add(prefix);
}
public void StartListen()
{
if (!listener.IsListening)
{
listener.Start();
Task.Factory.StartNew(async () =>
{
while (true) await Listen(listener);
}, TaskCreationOptions.LongRunning);
Console.WriteLine("Listener started");
}
}
public void StopListen()
{
if (listener.IsListening)
{
listener.Stop();
Console.WriteLine("Listener stopped");
}
}
private async Task Listen(HttpListener l)
{
try
{
var ctx = await l.GetContextAsync();
var text = "Hello World";
var buffer = Encoding.UTF8.GetBytes(text);
using (var response = ctx.Response)
{
ctx.Response.ContentLength64 = buffer.Length;
ctx.Response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
catch (HttpListenerException)
{
Console.WriteLine("screw you guys, I'm going home!");
}
}
}
Usage:
var x = new XListener("http://locahost:8080");
x.StartListen();
Thread.Sleep(500); // test purpose only
x.StopListen();
Thread.Sleep(500); // test purpose only
x.StartListen();
/* OUTPUT:
=> Listener started
=> Listener stopped
=> screw you guys, I'm going home!
=> Listener started */
You need to signal the thread to call HttpListener.Stop() and wait for the thread to finish by calling Thread.Join()
All you need to do is call stop on the listener. Since your listener thread is blocked on GetContext you will need to do this on another thread. IIRC this will cause GetContext to throw, so you will want to handle that exception and clean up. Calling Thread.Abort should be your last resort and wont cause the listener to stop listening until it is garbage collected anyway.
using System;
using System.Net;
using System.Text;
class Server
{
HttpListener listener = new HttpListener();
public Server(string url)
{
listener.Prefixes.Add(url);
}
void Callback(IAsyncResult result)
{
HttpListenerContext context = listener.EndGetContext(result);
byte[] buffer = Encoding.UTF8.GetBytes("Hello world!");
context.Response.ContentLength64 = buffer.Length;
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
listener.BeginGetContext(new AsyncCallback(Callback), listener);
}
public void Start()
{
listener.Start();
listener.BeginGetContext(new AsyncCallback(Callback), listener);
}
public void Stop()
{
listener.Stop();
}
public void Close()
{
listener.Close();
}
}

How to abort thread that use AcceptTcpClient() inside?

AcceptTcpClient() prevents app from exit after I called thrd.Abort().
How to exit application when in listening?
You should be able to interrupt the call to AcceptTcpClient() by closing the TcpListener (this will result in an exception being thrown by the blocking AcceptTcpClient(). You should not be aborting the thread, which is generally a very bad idea in all but a few very specific circumstances.
Here's a brief example:
class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Any, 12343);
var thread = new Thread(() => AsyncAccept(listener));
thread.Start();
Console.WriteLine("Press enter to stop...");
Console.ReadLine();
Console.WriteLine("Stopping listener...");
listener.Stop();
thread.Join();
}
private static void AsyncAccept(TcpListener listener)
{
listener.Start();
Console.WriteLine("Started listener");
try
{
while (true)
{
using (var client = listener.AcceptTcpClient())
{
Console.WriteLine("Accepted client: {0}", client.Client.RemoteEndPoint);
}
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("Listener done");
}
}
The code above starts a listener on a separate thread, pressing Enter on the console window will stop the listener, wait for the listener thread to complete, then the application will exit normally, no thread aborts required!
You could:
Use BeginAcceptTcpClient() and End.. instead:
See:https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.beginaccepttcpclient(v=vs.110).aspx
Or your could:
Create a TcpClient and send your listener message:
therefore (I guess you have a loop in your thread):
break the loop wherein the listener.AcceptTcpClient() is running.
(i.e. CancelAsync()) from outside and
Loop While (!Tread.CancellationPending);
Create a TcpClient and send your listener a message (and discard data);
TcpClient see: https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.110).aspx
Now your thread can go on with:
client.close() and listener.stop()

Simple Task-returning Asynchronous HtppListener with async/await and handling high load

I have created the following simple HttpListener to serve multiple requests at the same time (on .NET 4.5):
class Program {
static void Main(string[] args) {
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://+:8088/");
listener.Start();
ProcessAsync(listener).ContinueWith(task => { });
Console.ReadLine();
}
static async Task ProcessAsync(HttpListener listener) {
HttpListenerContext ctx = await listener.GetContextAsync();
// spin up another listener
Task.Factory.StartNew(() => ProcessAsync(listener));
// Simulate long running operation
Thread.Sleep(1000);
// Perform
Perform(ctx);
await ProcessAsync(listener);
}
static void Perform(HttpListenerContext ctx) {
HttpListenerResponse response = ctx.Response;
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
}
}
I use Apache Benchmark Tool to load test this. When I make a 1 request, I get the max wait time for a request as 1 second. If I make 10 requests, for example, max wait time for a response goes up to 2 seconds.
How would you change my above code to make it as efficient as it can be?
Edit
After #JonSkeet's answer, I changed the code as below. Initially, I tried to simulate a blocking call but I guess it was the core problem. So,I took #JonSkeet's suggestion and change that to Task.Delay(1000). Now, the below code gives max. wait time as approx. 1 sec for 10 concurrent requests:
class Program {
static bool KeepGoing = true;
static List<Task> OngoingTasks = new List<Task>();
static void Main(string[] args) {
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://+:8088/");
listener.Start();
ProcessAsync(listener).ContinueWith(async task => {
await Task.WhenAll(OngoingTasks.ToArray());
});
var cmd = Console.ReadLine();
if (cmd.Equals("q", StringComparison.OrdinalIgnoreCase)) {
KeepGoing = false;
}
Console.ReadLine();
}
static async Task ProcessAsync(HttpListener listener) {
while (KeepGoing) {
HttpListenerContext context = await listener.GetContextAsync();
HandleRequestAsync(context);
// TODO: figure out the best way add ongoing tasks to OngoingTasks.
}
}
static async Task HandleRequestAsync(HttpListenerContext context) {
// Do processing here, possibly affecting KeepGoing to make the
// server shut down.
await Task.Delay(1000);
Perform(context);
}
static void Perform(HttpListenerContext ctx) {
HttpListenerResponse response = ctx.Response;
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
}
}
It looks to me like you'll end up with a bifurcation of listeners. Within ProcessAsync, you start a new task to listen (via Task.Factory.StartNew), and then you call ProcessAsync again at the end of the method. How can that ever finish? It's not clear whether that's the cause of your performance problems, but it definitely looks like an issue in general.
I'd suggest changing your code to be just a simple loop:
static async Task ProcessAsync(HttpListener listener) {
while (KeepGoing) {
var context = await listener.GetContextAsync();
HandleRequestAsync(context);
}
}
static async Task HandleRequestAsync(HttpListenerContext context) {
// Do processing here, possibly affecting KeepGoing to make the
// server shut down.
}
Now currently the above code ignores the return value of HandleRequestAsync. You may want to keep a list of the "currently in flight" tasks, and when you've been asked to shut down, use await Task.WhenAll(inFlightTasks) to avoid bringing the server down too quickly.
Also note that Thread.Sleep is a blocking delay. An asynchronous delay would be await Task.Delay(1000).

Categories