更新 - 2021 年 11 月 1 日
大多数原始技术在 .NET Framework 4.7 及更高版本以及 .NET Core(包括 .NET 5 及更高版本)中作为开箱即用的方法提供。此时,.NET 6 已在生产中得到支持,并且目前对 .NET 的长期支持,两周后即可获得完整的 RTM。
await socket.ConnectAsync(someIp,somePort);
await socket.Send(msg, SocketFlags.None);
这些类通过可选的 CancellationToken 参数支持取消。
对于基于 Socket 的高性能代码,引入了管道以允许通过重用内存缓冲区以最小分配处理任意消息大小,这些缓冲区从一个处理步骤传递到下一个处理步骤。这既节省了内存,也节省了在缓冲区之间复制数据所需的时间。
WebClient 完全被 HttpClient 取代。事实上,WebClient 和 HttpWebRequest 类现在只是 HttpClient 的兼容性包装器。HttpClient 是线程安全和异步的,使用套接字和管道提供比 WebClient 更好的性能:
HttpClient _client=new HttpClient(...);
...
async Task<string> GetThatPage(string someUrl)
{
var page=await _client.GetStringAsync(someUrl);
....
}
在 .NET 6 中,Parallel.ForEachAsync
同时发出多个异步请求变得微不足道:
var urls=new List<string>();
...
await Parallel.ForEach(urls,async url=>{
var page=_client.GetStringAsync(url);
var fileName=CalculateNameFrom(url);
await File.WrileAllTextAsync(fileName,page);
});
.NET (Core) 中的默认编码是 UTF8。
var reply=await ping.SendPingAsync(someIP);
原始答案(2012)
Stephen Toub 在“等待套接字操作”中讨论了 .NET 4.5 的这个主题。通过一些工作,您可以对 .NET 4 使用相同的技术。
您可以使用任务并行库来简化异步调用,而不是使用回调。使用 TPL,您可以将所有套接字操作和回调转换为调用操作并处理结果的任务。
使用 ContinueWith 等方法可以非常轻松地组合任务,仅在第一个任务完成时执行链中的下一个任务。
任务是使用线程池中的线程执行的,因此您不必担心线程。
TPL 已经提供了一种从 BeginXXX/EndXXX 函数对或 IAsyncResult 对象创建任务的方法。如果您的套接字库提供了这些,您可以立即开始使用任务。
要处理事件,您可以使用任务并行库中的 TaskCompletionSource 创建一个任务,该任务将在您调用套接字方法时启动,并仅在引发相应事件时完成。该技术在“任务和基于事件的异步模式”中进行了描述
ParallelExtensionsExtras库使用此技术为WebClient、SmtpClient 和 Ping提供异步方法版本。
使用“Tasks and the EAP”中的代码,您可以为您的 Socket 类编写扩展方法,如下所示:
public static Task ConnectTask( this Socket socket, object address)
{
var tcs = CreateSource(address);
socket.ConnectCompleted +=
(sender, e) => TransferCompletion(tcs, e, () => e.Result, null);
socket.ConnectAsync(address, tcs);
return tcs.Task;
}
并像这样使用它:
var connectTask=mySocket.ConnectTask(myaddress);
connectTask.ContinueWith(t=> { ... });
在 .NET 4.5 中事情变得更加容易。async/await 关键字允许您取消 ContinueWith 调用并编写看起来与其同步版本非常相似的代码。您可以检查“等待套接字操作”以获得专门针对套接字的扩展。