通过async / await进行服务器通信?

我想通过async / await创建通过TAP发送Socket消息。

在阅读了这个答案之后 – 我决定创建一个完全正常的样本:

那我尝试了什么:

我从这里获取了TAP扩展方法(一切正常):我在控制台cmd中测试它:

接收代码

public static class SocketExtensions { public static Task ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count) { return Task.Factory.FromAsync( socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket), socket.EndReceive); } public static async Task ReceiveExactTaskAsync(this Socket socket, int len) { byte[] buf = new byte[len]; int totalRead = 0; do{ int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead); if (read <= 0) throw new SocketException(); totalRead += read; }while (totalRead != buf.Length); return buf; } public static Task ConnectTaskAsync(this Socket socket, string host, int port) { return Task.Factory.FromAsync( socket.BeginConnect(host, port, null, null), socket.EndConnect); } public static Task SendTaskAsync(this Socket socket, byte[] buffer) { return Task.Factory.FromAsync( socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket), socket.EndSend); } } static void Main() { Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); s.ConnectTaskAsync("127.0.0.1", 443); var buf1 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes Console.Write(Encoding.UTF8.GetString(buf1.Result)); var buf2 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes Console.Write(Encoding.UTF8.GetString(buf2.Result)); Console.ReadLine(); } 

发件人代码:

 // use same extension method class like above ....^ void Main() { Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp); s.ConnectTaskAsync( "127.0.0.1" , 443); s.SendTaskAsync(Encoding.UTF8.GetBytes("hello")); s.Close(); Console.ReadLine(); } 

注意我从main中删除了async ,因为我在控制台中测试它。

题 ,

根据上面的链接,代码应该工作

但是我没有例外,它只是挂在那条线上:

Console.Write(Encoding.UTF8.GetString(buf1.Result));

(首先我运行接收器,然后运行发送器)

我错过了什么?

问题来自“ 通知我从主程序中删除了异步,因为我在控制台中测试它。 ”。

在执行下一步之前,您需要等待操作完成。 您作为示例使用的代码暂停, await操作完成,您的代码直接通过。

您可以通过在每个具有.Wait()操作之后放置.Wait()或通过Task.Run(在线程Task.Run(线程中运行此函数来解决此Task.Run( 但是我认为最好知道何时应该使用异步,什么时候不应该。

当你有其他可以让线程做的工作时,应该使用异步 ,通常“其他工作”将是在WinForms项目上处理UI消息或接受ASP.NET站点上的新连接。 在控制台应用程序中,程序在等待时没有其他工作,因此在这种情况下使用函数的同步版本会更合适。


PS你发表了“ 这就是为什么我删除异步等待并使用了Task.result ”之后发表了评论,只是让你知道从来没有结合使用await代码和阻止同步竞争的代码(通过使用像Task.Result这样的Task.ResultTask.Wait() ,您可能会导致代码死锁并停止运行!

这不是您当前示例的问题,因为控制台应用程序没有同步上下文,但如果您将此代码复制并粘贴到可以轻松锁定程序的内容中。

1:好的,你可以结合await和阻止代码,但是你需要遵循规则,但是如果你足够了解我的话,我知道你知道的足够安全。 如果您不知道如何安全地做到这一点,请避免这样做

因为你不等待线程完成他们的工作,然后调用s.Close() ,套接字将在任何流量发送之前关闭。 您必须删除s.Close()调用或等待调用完成,例如通过

  Task connect = s.ConnectTaskAsync( "127.0.0.1" , 443); connect.Wait(); // you have to connect before trying to send Task sendData = s.SendTaskAsync(Encoding.UTF8.GetBytes("hello")); sendData.Wait(); // wait for the data to be sent before calling s.Close() s.Close(); 

或者您可以在方法中将其打包并等待该方法完成。 最终结果是在完成之前的呼叫之前不要调用Close

  void Main() { Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp); Task worker = DoWorkAsync(s); worker.Wait(); s.Close(); Console.ReadLine(); } private async Task DoWorkAsync(Socket s){ await s.ConnectTaskAsync( "127.0.0.1" , 443); await s.SendTaskAsync(Encoding.UTF8.GetBytes("hello")); }