【问题标题】:Socket Listener Multiple Simultaneous AcceptAsync Calls套接字侦听器多个同时 AcceptAsync 调用
【发布时间】:2016-04-07 21:26:46
【问题描述】:

所以我一直在研究套接字编程并阅读创建高性能服务器的方法。

阅读关于使用SocketAcceptAsync 方法,以及如果连接立即可用,有时调用可能如何同步执行(并返回false),以及如何在接受连接后将套接字重新置于侦听状态,我开始怀疑这是否真的意味着,尽管使用AcceptAsync 接受连接的套接字会异步“等待”,但最终它一次只会处理 1 个新连接,即使一次连接 1000 个客户端,也取决于多快客户端可能会被传递到另一个线程进行处理,这可能会限制性能。

这让我想知道我是否可以考虑到这一点来编写我的类,对所有连接使用 SocketAsyncEventArgs 的单个实例,而不是我最初打算使用的对象池。

考虑到这一点,我想知道如果在没有可用连接的情况下多次调用AcceptAsync 会发生什么。所以我做了这个非常粗略的测试应用程序,看看会发生什么:

class Program
{
    private static Socket s;

    static void Main(string[] args)
    {
        s = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        s.Bind(new IPEndPoint(IPAddress.Any, 10001));
        s.Listen(10);
        var e = new SocketAsyncEventArgs();
        e.Completed += EOnCompleted;
        s.AcceptAsync(e);
        e = new SocketAsyncEventArgs();
        e.Completed += EOnCompleted;
        s.AcceptAsync(e);
        e = new SocketAsyncEventArgs();
        e.Completed += EOnCompleted;
        s.AcceptAsync(e);
        e = new SocketAsyncEventArgs();
        e.Completed += EOnCompleted;
        s.AcceptAsync(e);
        while (true)
        {

        }
    }

    private static void EOnCompleted(object sender, SocketAsyncEventArgs socketAsyncEventArgs)
    {
        Console.WriteLine(socketAsyncEventArgs.AcceptSocket.RemoteEndPoint);
        Thread.Sleep(10000);
        var e = new SocketAsyncEventArgs();
        e.Completed += EOnCompleted;
        s.AcceptAsync(e);
    }
}

我使用一个 tcp 测试应用程序向应用程序发送连接垃圾邮件,结果显示多个 AcceptAsync 调用创建了多个似乎并行执行的接受/完成循环。

我的问题是,这真的有什么好处吗?这是允许更快处理传入连接的合法方式吗?有什么缺点吗?

编辑 1:进一步研究,如果数据始终可用于处理异步调用,那么它们将全部同步运行并且永远不会返回接受新连接,或者至少,在技术上似乎有可能严重延迟。

如果确实如此,您如何生产高性能服务器?

【问题讨论】:

    标签: c# sockets network-programming


    【解决方案1】:

    通常,只需要以下代码:

    while (true) {
     var socket = listener.Accept();
     Task.Run(async () => await RunConnectionAsync(socket));
    }
    

    此代码的用户模式部分将运行得非常快。几乎所有时间都花在内核上。这将很快接受连接。由于内核管理的积压,不会丢弃任何连接。

    很少需要多次未完成的接受调用来达到您的性能目标。如果您确实需要,只需在多个线程上运行相同的代码即可。

    SocketAsyncEventArgs API 即将过时。基于任务的 IO 更方便,而且 CPU 密集度更高。

    特别是,如果您为每个操作创建一个新的SocketAsyncEventArgs,您将失去该模式的所有好处。那是没有意义的。

    在几乎所有情况下,您都不需要异步 IO 来从套接字接受。它会花费更多的 CPU 时间来执行,因此延迟会稍长。

    【讨论】:

    • 谢谢,这很有帮助,互联网上充斥着答案和 10 页帖子,说明几乎每一种不同的方法都是最好的方法。我唯一关心的是是否适合将Task 用于长时间运行的任务。我的服务器将长时间打开数千个连接。如果我对每个套接字使用完全同步的访问,那将意味着我将有数千个任务一直在运行。这可以接受吗?
    • 这是使用 await 的异步套接字 IO 的绝佳案例。您可以通过这种方式拥有 1000 个连接(不消耗线程)。但是,接受循环只是 1 个线程。没人在乎。做你方便的事,通常是同步 IO。
    • 因此,使用 async IO 和 await 实现 RunConnectionAsync。接受循环在这个答案中是正确的。
    • 感谢您的澄清。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多