9

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

  • 以异步方式处理多个客户端套接字的框架
  • 每个传入消息协议规定每个消息都是格式字符串,并用换行符“\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);
                }
            });
    }

}
4

2 回答 2

14

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

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

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

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

于 2013-02-25T17:30:23.037 回答
1

根据我的建议,请使用新的异步形式XXXReceive和“XXXSend”(其中 XXX 代表BeginEnd),可用的新方法是ReceiveAsyncSendAsync方法,它们使用SocketAsyncEventArgs来传递回调事件处理程序上的套接字和其他信息。

我在 msdn 存档上看到了一个很好的套接字客户端和服务器的工作示例,该示例可扩展至 500 个连接(正如我在其中一个项目改编中测试过的那样),但目前我无法从谷歌搜索中找到该链接.. 但在这里是来自 msdn 存档的关于同一主题的另一个链接,希望它对您有所帮助 - Get Closer to the Wire with High-Performance Sockets in .NET ..

更新

首先,只能为您提供最终实现的想法,并尽可能提供一些简短的示例代码片段。好的,我在这里提供更多详细信息

我想你可以跳到最后一段的链接。;)

让我强调一次,因为我想,我说SendAsyncReceiveAsync不是BeginReceive/EndReceiveBeginSend/EndSend即基于事件的异步模式(EAP)

使用异步形式的 Socket 方法的好处是它们是套接字编程的无异常方法,可以证明比 BeginSend/EndSend 方法更快。

这是我发现最多可用于 500 个并行连接的示例的链接 - Networking Samples for .NET v4.0

因为您需要使用 .NET 4.5 的等待/异步功能。这是 .NET 4.5 代码片段,显示了WebSocket类的用法,其中也可以适应Socket实现 -支持 WebSockets 协议(我猜 WebSocketAspNetWebSocketContext将是 Socket 的SocketAsyncEventArgs

我从MSDN - Parallel Programming 团队博客中找到了Awaiting Socket Operations示例代码,可用于从 .NET 4.5 框架实现 await/async。

我希望这对您有所帮助。

于 2013-02-25T05:54:51.863 回答