我想在.NET Core 2.0中使用异步创建一个简单的TCP服务器(因为根据我的理解,它比产生线程更合理)采用async/await
方法(因为我认为它更符合 - 日期比使用IAsyncResult
和*Begin/*End
方法的日期。
我写过这个小服务器接受来自客户端的新连接,然后开始发送100条消息(它们之间有1秒的延迟)。
主要问题是:
如果我没有产生新线程,那么服务器如何继续向多个客户端发送延迟消息,实际上它是“等待连接”? 是否涉及任何隐藏的低级信号/事件,或者是否真的只有新线程?
第二个问题是:
如果我没有使用这个全新的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();
}
}
原始代码和下面的版本有什么区别? 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();
}
}
答案 0 :(得分:0)
主要问题的答案:
在后台,通常,对象使用一些api(winapi for windows)。这些api可以不同地实现异步。例如,事件(winapi事件)或回调。所以答案是肯定的 - 有隐藏的信号或线程。例如,您可以看到 Ping 类。 Ping.InternalSend
使用ThreadPool.RegisterWaitForSingleObject
执行异步任务。
关于async Main的答案:
在您的第一个代码中,由于StartServer
不是异步,Main
方法在您接受&#34;接受&#34;之前无法获得控制权。循环结束。
在第二个代码中,Main
方法将返回控制权,然后调用listener.AcceptTcpClientAsync
。但是因为您使用await StartServer();
,Main方法将等到StartServer
结束。
一些代码可以解释:
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");
}
}
}
此代码生成此输出:
StartServer输入
主StartServer完成了 SendHundredMessages输入
SendHundredMessages输入
SendHundredMessages退出
SendHundredMessages输入
StartServer出口
task.Wait()完成了 SendHundredMessages退出
SendHundredMessages退出
如您所见,Main
方法在第一个Task.Delay
之后立即继续执行。
警告:
你不等待SendHundredMessages
结束,这非常糟糕。在示例输出中,您可以看到&#34; SendHundredMessages结束&#34;在&#34; task.Wait()完成后&#34;。在示例应用中当然不是危险,但在实际项目中你可能会遇到大问题。