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

时间:2013-02-24 07:05:38

标签: 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);
                }
            });
    }

}

2 个答案:

答案 0 :(得分:13)

目前没有现行的标准或惯例。你有很多选择,每个选择都有优点和缺点:

  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个套接字类。
    • 优点:非常易于使用;一切都是事件,没有多线程问题。此外,状态管理的琐事部分也是为您完成的。
    • 缺点:不能像低级解决方案一样扩展。
  6. 根据您的情况(少于一百个套接字,每秒几百条消息),我建议使用我的Nito.Async库。这是最容易使用的选项之一。

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

答案 1 :(得分: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)

  

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

以下是我发现最多500个PARALLEL连接的示例链接 - Networking Samples for .NET v4.0

因为您需要使用.NET 4.5的await / async功能。以下是.NET 4.5代码片段,其中显示了WebSocket类的用法,其中也可以适用于Socket实现 - Support for WebSockets Protocol(我猜WebSocket的AspNetWebSocketContext将是Socket的{{ 1}})

我发现了来自Awaiting Socket OperationsMSDN - Parallel Programming team blog示例代码,可用于从.NET 4.5框架实现await / async。

我希望这证明对你有帮助。