C# 通过异步/等待进行服务器通信?

C# 通过异步/等待进行服务器通信?,c#,task-parallel-library,async-await,asp.net-4.5,C#,Task Parallel Library,Async Await,Asp.net 4.5,我想通过异步/等待创建通过TAP发送的套接字消息 在阅读和-之后,我决定创建一个完全工作的示例: 那么,我尝试了什么: 我从(所有ok)中获取了TAP扩展方法,并在console cmd中对其进行了测试: 接收器代码 public static class SocketExtensions { public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int

我想通过异步/等待创建通过TAP发送的套接字消息

在阅读和-之后,我决定创建一个完全工作的示例:

那么,我尝试了什么:

我从(所有ok)中获取了TAP扩展方法,并在console cmd中对其进行了测试:

接收器代码

public static class SocketExtensions
{
    public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
    {
        return Task.Factory.FromAsync<int>(
                         socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
                         socket.EndReceive);
    }

    public static async Task<byte[]> 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<int>(
                         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();
}
注意,我从main中删除了
async
,因为我在控制台中测试它

问题:

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

但我也不例外,只是挂在这条线上:

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

(首先我运行receiver,然后运行sender)


我遗漏了什么?

因为您没有等待线程完成工作,然后调用
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();
或者您可以将其放入一个方法中,等待该方法完成。最终结果是,在完成之前的调用之前,不调用
关闭

 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"));
}

由于您不必等待线程完成其工作,然后调用
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();
或者您可以将其放入一个方法中,等待该方法完成。最终结果是,在完成之前的调用之前,不调用
关闭

 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"));
}

问题来自于“注意,我从main中删除了异步,因为我在控制台中测试它。”

在执行下一步之前,需要等待操作完成。您用作示例的代码在每次
等待操作完成时都会暂停,您的代码只需直接通过即可

您可以通过在每个操作之后放置
.Wait()
来修复此问题,该操作本应具有
Wait
,或者通过
Task.Run(
)在线程池线程内运行此函数,但是我认为最好知道何时应该使用异步以及何时不应该使用异步

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


另外,在我发布“这就是为什么我删除了async Waitis and used Task.result”之后,您发表了评论,只是为了让您知道,决不要将使用
Wait
的代码与阻止同步竞赛的代码(通过使用
Task.result
Task.Wait()等方式)组合在一起
,可能会导致代码死锁并停止运行

对于您当前的示例来说,这不是一个问题,因为控制台应用程序没有同步上下文,但是如果您将此代码复制并粘贴到某个同步上下文上,您可以轻松地锁定程序


1:好的,你可以把等待和阻塞代码结合起来,但是你需要遵守一些规则,但是如果你知道足够多的知识来反驳我的话,你就知道足够安全地去做。如果你不知道如何安全地去做,那就避免去做它

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

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

您可以通过在每个操作之后放置
.Wait()
来修复此问题,该操作本应具有
Wait
,或者通过
Task.Run(
)在线程池线程内运行此函数,但是我认为最好知道何时应该使用异步以及何时不应该使用异步

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


另外,在我发布“这就是为什么我删除了async Waitis and used Task.result”之后,您发表了评论,只是为了让您知道,决不要将使用
Wait
的代码与阻止同步竞赛的代码(通过使用
Task.result
Task.Wait()等方式)组合在一起
,可能会导致代码死锁并停止运行

对于您当前的示例来说,这不是一个问题,因为控制台应用程序没有同步上下文,但是如果您将此代码复制并粘贴到某个同步上下文上,您可以轻松地锁定程序


1:好的,你可以把等待和封锁代码结合起来,但是有一些规则你需要遵守,但是如果你知道足够多的知识来反驳我的话,你就知道足够安全地去做。如果你不知道如何安全地去做,那就避免去做它。

它与
127.0.0.1
一起工作吗?你确定没有防火墙/路由器封锁吗你的ip地址的流量?@默认这是我自己的ip,就像127.0.0.1一样(我在给自己打电话)@Default也用127.0.0.1进行了测试…仍然不工作。(也有问题更新)你可以下载Wireshark,看看是否有流量流出。另一个技巧是Sysinternals中的TcpView,它显示打开的端口和打开它们的程序。我有一个更简单的代码,它确实可以工作,但它不使用tap、async/await。所以没有什么