草庐IT

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

coder 2023-09-17 原文

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

  • 以异步方式处理多个客户端套接字的框架
  • 每个传入消息协议(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# - 当前实现异步套接字客户端的实践标准是什么?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐