【问题标题】:How to make synchronous socket asynchronous?如何使同步套接字异步?
【发布时间】:2014-02-08 06:50:06
【问题描述】:

我必须使用只公开同步套接字接口的套接字库。我有几个问题将此同步转换为异步。我从msdn示例开始(库接口类似于microsoft socket库,只是库使用了不同的通信算法)。

这是我目前的进度

    private async void Start_Click(object sender, RoutedEventArgs e)
    {
        Log("Start server");
        var port = int.Parse(Port.Text);
        var task = Task.Run(() =>
        {
            // Establish the local endpoint for the socket.
            // Dns.GetHostName returns the name of the 
            // host running the application.
            Log("Hostname " + Dns.GetHostName());

            IPAddress[] ipv4Addresses = Array.FindAll(
                Dns.GetHostEntry(string.Empty).AddressList,
                a => a.AddressFamily == AddressFamily.InterNetwork);
            IPAddress[] ipv6Addresses = Array.FindAll(
                Dns.GetHostEntry(Dns.GetHostName()).AddressList,
                a => a.AddressFamily == AddressFamily.InterNetworkV6);

            foreach (var ip in ipv4Addresses)
            {
                Log("Ipv4 list " + ip);
            }
            foreach (var ip in ipv6Addresses)
            {
                Log("Ipv6 list " + ip);
            }

            var localEndPoint = new IPEndPoint(IPAddress.Any, port);
            Log("Ip " + IPAddress.Any);
            Log("Port " + port);

            // Create a TCP/IP socket.
            var listener = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);

            // Bind the socket to the local endpoint and 
            // listen for incoming connections.
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen((int)SocketOptionName.MaxConnections);
                Log("Max connection " + (int)SocketOptionName.MaxConnections);

                // Start listening for connections.
                bool isContinue = true;
                while (isContinue)
                {
                    Log("Waiting for a connection...");
                    // Program is suspended while waiting for an incoming connection.
                    var handler = listener.Accept();


                    // handle connection
                    // Data buffer for incoming data.
                    byte[] bytes = new Byte[1024];

                    string data;

                    Log("Connection accepted");

                    Log("Local endpoint " + handler.LocalEndPoint);
                    Log("Remote endpoint " + handler.RemoteEndPoint);

                    data = null;

                    // An incoming connection needs to be processed.
                    while (true)
                    {
                        bytes = new byte[1024];
                        int bytesRec = handler.Receive(bytes);
                        data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                        if (data.IndexOf("<EOF>") > -1)
                        {
                            break;
                        }
                    }

                    // Show the data on the console.
                    Log("Text received " + data);

                    // Echo the data back to the client.
                    byte[] msg = Encoding.ASCII.GetBytes(data);

                    handler.Send(msg);
                    Log("Message sent");

                    handler.Shutdown(SocketShutdown.Both);
                    Log("Shutdown");
                    handler.Close();
                    Log("Close");

                    isContinue = true;
                }

            }
            catch (Exception ex)
            {
                Log(ex.ToString());
            }
        });
        await task;
    }
  1. 当我有 2 个或更多并发客户端时会发生什么?
  2. 当第一个连接忙于通信时,第二个连接发生了什么?
  3. 监听器在 listener.Accept() 之后是否立即监听?
  4. 假设我有 4 个逻辑核心,当我在 4 个线程上侦听每个具有不同端口号的线程时会发生什么。每个侦听器都阻塞了 listener.Accept 中的线程。我的 ui 和 pc 会发生什么?它会挂起(所有核心都被使用和阻塞)吗?
  5. 有什么模式可以参考吗?

我正在考虑使用 Task、async、await,但我似乎无法在脑海中构建它。

我想通过添加套接字来改进这个库。

谢谢。

【问题讨论】:

  • 感谢您的链接。
  • 那么这个第 3 方库是否暴露了Begin/End 风格的 API?
  • 如果该代码来自 MSDN,他们应该删除该代码。不安全的处理,ASCII 编码,奇怪的 DNS 东西没有一点。不要太相信 MSDN。
  • @usr 在这方面我必须同意你的看法。

标签: c# sockets asynchronous


【解决方案1】:

如果库只公开一个同步实现,这意味着它正在执行阻塞 I/O。由于您无法更改它,因为它是库实现的一部分,因此您必须使用 ThreadPool 线程来委派工作。我写了几篇博文,提供了相关背景(12)。

只有在您想特别避免阻塞特定线程(例如 UI 线程)时,异步处理这项工作才有意义。在这种情况下,您可以像这样简单地调用套接字库:

var task = Task.Run(() => 
  { 
     // run socket code
  });

var result = await task;
// rest of your code

Task.Run queues 的调用对 ThreadPool 起作用,并且 await 避免了在单独线程中执行该工作时阻塞 UI 线程。

【讨论】:

  • 使用线程池进行自然异步操作是个坏主意,如果可以避免的话:blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx
  • @Noseratio 我完全同意。这就是为什么我澄清应该这样做,因为库是阻塞的,并且只有当一个特定的线程不能被阻塞时。你觉得这有意义吗?
  • 确实有道理。我没有意识到涉及到第 3 方库,因为 OP 在他的代码中只使用标准套接字。我现在对你的答案投了赞成票。
  • @publicENEMY 再次,这取决于您是否尝试确保 UI 线程仍然响应。如果你是,那么是的。否则,这取决于您为什么要从调用者的角度使此调用异步。
  • @publicENEMY 异步套接字服务器是否需要异步,因为它需要执行非阻塞 I/O?这不会发生,因为正如你所说,图书馆是同步的。你读过msdn.microsoft.com/en-us/library/windows/desktop/…吗?您唯一能做的就是避免阻塞特定线程或一组线程并将工作分派给后台线程。这是我在回答中写的,但我不认为我们互相理解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多