[英]Understanding async behaviour of C# console TCP server
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). 我想通过
async/await
方法(使用异步(因为据我所知,它比生成线程更合理))在async/await
方法中在.NET Core 2.0中创建一个简单的TCP服务器(因为我认为它比使用async/await
方法更为最新) IAsyncResult
和*Begin/*End
方法)。
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). 我已经编写了这个小型服务器,该服务器接受来自客户端的新连接,然后开始向它们发送100条消息(它们之间的延迟为1s)。
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? 如果我没有使用这种全新的
async Main
语法糖,并且我没有在“等待”发送消息的async
任务-我是否正确使用了异步?
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? async Main
什么区别?
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). 通常,在后台,对象使用某些api(用于Windows的winapi)工作。 These api can implement asynchrony differently.
这些api可以不同地实现异步。 For example, events(winapi events) or callbacks.
例如,事件(winapi事件)或回调。 So the answer is yes - there are hidden signals or threads.
因此答案是肯定的-存在隐藏的信号或线程。 For example, you can see the Ping class.
例如,您可以看到Ping类。
Ping.InternalSend
using ThreadPool.RegisterWaitForSingleObject
for the task of asynchrony. 使用
ThreadPool.RegisterWaitForSingleObject
执行Ping.InternalSend
以实现异步任务。
The answer about async Main: 关于异步Main的答案:
In your first code because StartServer
is not async the Main
method will not get back control until your "accept" cycle ends. 在您的第一个代码中,因为
StartServer
不异步,所以在您的“接受”周期结束之前, Main
方法不会获得控制权。
In your second code, the Main
method will get back control then listener.AcceptTcpClientAsync
invoked. 在第二个代码中,
Main
方法将先获得控制权,然后再调用listener.AcceptTcpClientAsync
。 But because you using await StartServer();
但是因为您使用
await StartServer();
the Main method will be wait until StartServer
ends. Main方法将等待,直到
StartServer
结束。
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
StartServer输入
Main StartServer finished主启动服务器完成
SendHundredMessages enterSendHundredMessages输入
SendHundredMessages enterSendHundredMessages输入
SendHundredMessages exitSendHundredMessages退出
SendHundredMessages enterSendHundredMessages输入
StartServer exitStartServer退出
task.Wait() finishedtask.Wait()完成
SendHundredMessages exitSendHundredMessages退出
SendHundredMessages exitSendHundredMessages退出
As you can see, the Main
method continued execution right after first Task.Delay
. 如您所见,
Main
方法在第一个Task.Delay
之后Task.Delay
继续执行。
A warning: 一个警告:
You do not wait end of SendHundredMessages
, and this is very bad. 您不必等待
SendHundredMessages
结束,这非常糟糕。 In example output you can see that " SendHundredMessages ending " after " task.Wait() finished ". 在示例输出中,您可以看到“ task.Wait()完成之后”,“ SendHundredMessages结尾 ”。 In example application of course it not danger, but in real project you can get big problem.
在示例应用程序中当然没有危险,但是在实际项目中您会遇到很大的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.