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

Mat*_*olf 8 c# sockets asynchronous tcp networkstream

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

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

我想知道我使用的框架BeginReceiveEndReceive实现的IAsyncResult回调是否是最先进的,因为我的目标是.Net 4.5.有没有更好的解决方案,例如使用NetworkStream或其他API选择?使用BeginReceive/EndReceive实现真正让我感到困惑的是,在EndReceive之后,我必须再次调用BeginReceive并再次注册回调.这对我来说听起来像是一个可怕的开销.为什么不能将新数据异步在任何时候,在同一时间增加了另一个环境中建立被然后通过引发的事件路由的完整信息?

使用IAsyncResult的论点通常是在处理线程处理时给出的,但是反对以下内容:使用NetworkStream并简单地读取和写入流.如上所述,只交换字符串消息,每个协议的每条消息都由换行符标记完成.单独的任务/线程将轮询流式读取器(基于网络流)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);
                }
            });
    }

}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 13

目前没有现行的标准或惯例.您有多种选择,每种选择都有优点和缺点:

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

对于你的情况(少于一百个套接字每秒几百条消息),我建议使用我的Nito.Async库.这是最容易的选择之一.

关于你的协议,你必须\n手工解析s并做自己的缓冲.(对于上述所有选择都是如此).