1

我正在使用第三方套接字库,尽管这与我的问题有些无关。该库包含一个带有“ConnectAsync”和“WriteAsync”等方法的“Socket”类。调用这些方法之一后控制立即返回,并且套接字类引发事件以通知操作何时完成,例如“ConnectCompleted”和“WriteCompleted”。当接收到数据时,该类还会引发一个事件:“PacketArrived”。

在我的应用程序中,我想发送数据并等待回复。由于这可能发生在很多地方,因此将上面的套接字类包装在某种帮助器中为我完成所有这些是有意义的。我还希望它是异步的,因此 UI (WPF) 在连接/写入/等待回复期间不会冻结,因此辅助方法调用可能如下所示:-

SocketHelper.SendData(dataToSend, myCallback);

当接收到数据时调用“myCallback”。

作为线程新手,我不知道如何编写这个助手 - 不知何故,它需要将各种方法和事件串在一起,即打开连接,等待连接完成,写入数据,等待写入完成,然后等待数据包到达(然后调用提供的回调)。任何帮助表示赞赏。

4

1 回答 1

2

更新 - 2021 年 11 月 1 日

大多数原始技术在 .NET Framework 4.7 及更高版本以及 .NET Core(包括 .NET 5 及更高版本)中作为开箱即用的方法提供。此时,.NET 6 已在生产中得到支持,并且目前对 .NET 的长期支持,两周后即可获得完整的 RTM。

  • SocketTaskExtensions类为Sockets提供基于任务的扩展方法,例如ConnectAsync允许异步打开连接,而SendAsync异步发送缓冲区:
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 调用并编写看起来与其同步版本非常相似的代码。您可以检查“等待套接字操作”以获得专门针对套接字的扩展。

于 2012-06-15T10:47:53.957 回答