【问题标题】:Trouble with Task.WhenAnyTask.WhenAny 的问题
【发布时间】:2019-10-16 02:51:34
【问题描述】:

我正在使用 .NET 框架来创建一个服务器,它监听本地主机上的两个端口。这是一个简单的控制台应用程序。

当我继续连接到其中一个端口时它可以工作,但是在我第一个连接后,另一个没有响应。第一个还活着。

这是我的代码:

    static void Main(string[] args)
    {
        IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
        List<TcpListener> listeners = new List<TcpListener>()
        {
            new TcpListener(hostIP, 6060),
            new TcpListener(hostIP, 6061)
        };
        foreach (TcpListener listener in listeners)
        {
            listener.Start();
        }
        try
        {
            while (true)
            {
                Socket socket = AcceptAnyConnection(listeners).Result;
                NetworkStream stream = new NetworkStream(socket);
                byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
                stream.Write(bytes, 0, bytes.Length);
                //stream.Close();
                socket.Close();
            }
        }
        finally
        {
            foreach (TcpListener listener in listeners)
            {
                listener.Stop();
            }
        }
    }

    private static async Task<Socket> AcceptAnyConnection(List<TcpListener> listeners)
    {
        List<Task<Socket>> tasks = new List<Task<Socket>>();
        foreach (TcpListener listener in listeners)
        {
            tasks.Add(AcceptConnection(listener));
        }
        Task<Socket> completedTask = await Task.WhenAny(tasks);
        return await completedTask;
    }
    private static async Task<Socket> AcceptConnection(TcpListener listener)
    {
        Socket socket = await listener.AcceptSocketAsync();
        return socket;
    }

如果我连接到另一个端口,则等待 Task.WhenAny() 阻塞。

我一定是做错了什么,但我不确定是什么。

顺便说一句,我确实尝试过使用 .NET Core 控制台应用程序,它工作正常。

谢谢

【问题讨论】:

  • 这很复杂,但如果你不使用.Result 而使用await,它会起作用……让你的主函数成为异步任务。
  • 一个问题是,每当一个监听器获得一个新的连接时,你都会在所有监听器上调用AcceptSocketAsync(并丢弃你上次调用它时之前未完成的任务)。相反,您可能希望维护一个 List&lt;Task&lt;Socket&gt;&gt;,并且每次其中一个完成时,将其从列表中删除,在任务完成的侦听器上调用 AcceptSocketAsync,并将新任务放入列表中
  • Archie,虽然以上两个答案都是正确的,但我建议您在这里深入研究事件模式,您可以让每个侦听器工作到一个单独的线程中,并且每当您找到连接或消息时都会触发一个事件到应用程序的另一层,您不会遇到这样的问题
  • 你绝对不能在任何你不知道要完成的任务上使用Result;这会使您的异步工作流变成同步工作流,然后可能会死锁!

标签: c# .net server async-await


【解决方案1】:

我建议将您的代码重构为类似的内容,然后从那里获取。也就是说,为每个侦听器运行一个无限循环。这避免了确保您每次只为每个客户调用一次 AcceptTcpClientAsync 的问题。

(请注意,没有代码可以真正停止侦听器。也完全未经测试 - 请仅将其用作指示采用哪种方法)

static void Main(string[] args)
{
    IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
    List<TcpListener> listeners = new List<TcpListener>()
    {
        new TcpListener(hostIP, 6060),
        new TcpListener(hostIP, 6061)
    };

    var listenerTasks = listeners.Select(x => RunTcpListener(x)).ToArray();
    Task.WaitAll(listenerTasks);
}

private static async Task RunTcpListener(TcpListener listener)
{
    listener.Start();

    try
    {
        while (true)
        {
            using (var client = await listener.AcceptTcpClientAsync())
            {
                var stream = client.GetStream();
                byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
                stream.Write(bytes, 0, bytes.Length);

                client.Close();
            }
        }
    }
    finally
    {
        listener.Stop();
    }
}

【讨论】:

  • 你好canton7!由于某种原因,您的代码对我不起作用,但您之前提出的将任务列表从无限循环中取出的建议 - 确实如此。我将在下面发布我的更新代码...谢谢!
  • @Archie 我刚刚逐字尝试了这段代码,它运行良好。 “对我不起作用”到底是什么意思?
【解决方案2】:

这是我的代码,在我遵循 canton7 的建议将任务列表移到循环之外并“在我们进行时”移动添加和删除任务之后。可能,我将不得不查看它以清理它,但这个想法有效:

    static void Main(string[] args)
    {
        IPAddress hostIP = Dns.GetHostAddresses("127.0.0.1")[0];
        List<TcpListener> listeners = new List<TcpListener>()
        {
            new TcpListener(hostIP, 6060),
            new TcpListener(hostIP, 6061)
        };
        ProcessConnections(listeners).Wait();
    }

    private async static Task SendData(Socket socket)
    {
        NetworkStream stream = new NetworkStream(socket);
        byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
        await stream.WriteAsync(bytes, 0, bytes.Length);
        socket.Close();
    }

    private static async Task ProcessConnections(List<TcpListener> listeners)
    {
        foreach (TcpListener listener in listeners)
        {
            listener.Start();
        }
        try
        { 
            List<Task<Socket>> tasks = new List<Task<Socket>>();
            foreach (TcpListener listener in listeners)
            {
                tasks.Add(AcceptConnection(listener));
            }
            while (true)
            {
                int i = Task.WaitAny(tasks.ToArray());
                Socket socket = await tasks[i];
                await SendData(socket);
                tasks.RemoveAt(i);
                tasks.Insert(i, AcceptConnection(listeners[i]));
            }
        }
        finally
        {
            foreach (TcpListener listener in listeners)
            {
                listener.Stop();
            }
        }
    }
    private static async Task<Socket> AcceptConnection(TcpListener listener)
    {
        Socket socket = await listener.AcceptSocketAsync();
        return socket;
    }

【讨论】:

  • (不用await tasks[i]——你知道它已经完成了,所以你可以做tasks[i].Result
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 2017-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多