c# - 当前实现异步套接字客户端的实践标准是什么?

标签 c# sockets asynchronous tcp networkstream

我寻找满足以下要求的最佳实践:

  • 以异步方式处理多个客户端套接字的框架
  • 每个传入消息协议(protocol)规定每个消息都是格式字符串,并用换行符“\n”标记为完整。
  • 我可以完全控制客户端而不是服务器端。服务器接受并发送带有换行符的基于字符串的消息,以标记消息的完成。
  • 我希望能够在任何给定时间通过每个连接的套接字发送消息(每个套接字都可以接收和发送消息)。
  • 应通过回调转发传入消息。
  • 我希望能够在我的实现中选择是将来自所有已连接套接字的所有传入完整消息路由到一个单独的回调,还是每个套接字客户端实现其自己的回调。
  • 我最多连接 4 个客户端/套接字。因此,我寻找能够利用如此有限数量的套接字的建议,然而,能够同时管理所有这些套接字。

我想知道我使用 BeginReceiveEndReceive 并实现 IAsyncResult 回调的框架是否是最先进的,因为我的目标是 .Net 4.5。是否有更好的解决方案,例如使用 NetworkStream 或其他 API 选择? BeginReceive/EndReceive 实现真正困扰我的是,在 EndReceive 之后我必须再次调用 BeginReceive 并再次注册回调。对我来说,这听起来像是一个可怕的开销。为什么不能随时异步添加新数据,同时另一个上下文构建完整的消息,然后通过引发的事件进行路由?

使用 IAsyncResult 的论据通常是在处理线程处理时给出的,但反对以下内容的是:使用 NetworkStream 并简单地读取和写入流。如前所述,仅交换字符串消息,并且每个协议(protocol)的每条消息都由换行符标记为完整。一个单独的任务/线程将通过 ReadLine() 轮询流读取器(基于网络流)。它可能不会比这更简单,不是吗?

我基本上想问的是,如何才能使以下代码真正异步?

public class SocketClient
{
    private TcpClient client;
    private StreamReader reader;
    private StreamWriter writer;

    public event Action<string> MessageCallback;

    public SocketClient(string hostname, int port)
    {
        client = new TcpClient(hostname, port);

        try
        {
            Stream stream = client.GetStream();
            reader = new StreamReader(stream);
            writer = new StreamWriter(stream);
            writer.AutoFlush = true;

            //Start Listener on port
            StartListener();
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }

    public void StartListener()
    {
        Task task = Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (MessageCallback != null)
                    {
                        MessageCallback(reader.ReadLine());

                    }

                    //Thread.Sleep(200);
                }
            });
    }

}

最佳答案

目前没有现行标准或通用做法。您有多种选择,每种选择都有优点和缺点:

  1. Wrap the TAP methods进入 Task 并使用 async/await
    • 优点:非常简单易行。
    • 缺点:级别低;您必须在“无限”循环中自行管理所有各种 async 操作,并处理每个连接的状态管理。
  2. Wrap the Socket *Async methods进入 Task 并使用 async/await
    • 优势:速度。这是最快和最具扩展性的选项。
    • 缺点:您仍然有低级“无限”循环和状态管理,其中代码比选项 (1) 更复杂。
  3. 将套接字完成转换为 Rx events .
    • 优点:您可以封装“无限”循环并将完成视为事件流。
    • 缺点:Rx 的学习曲线很陡,而且这不是一个简单的用例。管理每个连接的状态可能会变得复杂。
  4. 将套接字完成转换为 TPL Dataflow .
    • 优点:(与 Rx 相同):封装循环并获取数据流。
    • 缺点:学习曲线比 Rx 更容易,但您仍然需要对每个连接进行一些复杂的状态管理。
  5. 使用现有的库,例如我的 Nito.Async图书馆,提供EAP套接字类。
    • 优点:非常易于使用;一切都是事件,没有多线程问题。此外,状态管理的技巧部分已为您完成。
    • 缺点:扩展性不如低级解决方案。

对于您的情况(在不到一百个套接字上每秒有几百条消息),我建议使用我的 Nito.Async 库。这是这些选项中最容易开始工作的选项。

关于您的协议(protocol),您必须手动解析出 \n 并进行您自己的缓冲。 (以上所有选择都是如此)。

关于c# - 当前实现异步套接字客户端的实践标准是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15049496/

相关文章:

c# - 系统.绘图.打印 : The RPC server is unavailable

c# - MVC3将多个pdf作为zip文件返回

close() 没有正确关闭套接字

javascript - 如何在 Angular 中制作异步函数?

ios - 使用异步响应协议(protocol)

c# - 当观察变量改变时暂停执行?

c# - 为什么我不能将默认值分配给我的 notnull 泛型类型?

linux - 通过单个套接字发送的 UDP 数据包是否保证按顺序发送?

c - 处理/定义 poll 系统调用的 struct pollfd 数组的有效方法

Angular2 - 异步依赖注入(inject)