代码之家  ›  专栏  ›  技术社区  ›  Frinavale

在服务器推送应用程序中维护.NET异步套接字连接

  •  2
  • Frinavale  · 技术社区  · 15 年前

    我已经实现了一个使用异步套接字将信息从服务器推送到所有连接的Silverlight客户端的解决方案。

    我在推送服务器上遇到了一些问题,需要澄清一下,因为这是我第一次在完全异步的环境中使用套接字。

    现在,推送服务器接受新的套接字连接,并将它们添加到通用列表中。通用列表是我实现的用于管理单个异步套接字连接的自定义类型(名为AsyncClientConnection)。

    推送服务器有一个计时器,当经过指定的时间后,它将打开一个文件并将内容发送到所有连接的套接字(在通用列表中)。它模拟了未来会发生什么,当服务器从物理设备接收原始字节数据时。当服务器向客户机发送数据时,它将清除所有断开连接/释放的客户机。

    我的问题是通用列表,它不是线程安全的,在这个异步场景中不能很好地工作。当一个新的套接字连接到服务器时,会为其创建一个新的AsyncClientConnection,并将其添加到通用列表中;但是如果服务器正在将文件推送到已连接的客户机中,则会使用通用列表,因此当将新的AsyncClientConnection添加到列表中时,会修改该列表,并且会出现一个明显的异常。ASONS

    我以前从来没有认真地使用过线程(我只是试过确保我理解线程的理论),也从来没有使用过异步套接字,所以当涉及到可以用来解决问题的工具时,我有点迷失了方向。

    我甚至怀疑是否有不同的方法来将AsyncClientConnection类型维护在一起。

    任何建议都很好!

    谢谢,

    -弗林尼

    2 回复  |  直到 15 年前
        1
  •  1
  •   Frinavale    15 年前

    最初,我通过同步访问用于维护连接的资源来解决了我的问题。每当我添加一个新的连接时,我都锁定了这个资源(通用列表)。我还锁定了资源,每当我通过它枚举时,同时将数据推送到连接的套接字。

    在C中,我会使用的关键字是 ;因为我在使用vb.net,所以我使用了 同步时钟 关键字。这个 同步时钟 ,关键字防止多个线程访问线程资源。只要每段代码在使用前都锁定了资源,这就可以工作。

    这个解决方案可以工作;但是,正如Doug指出的,新的连接必须等待服务器完成将数据推送到连接的客户机。随着服务器推送数据的频率增加,连接的客户机数量增加,我开始注意到新客户机连接需要一段时间。

    这个问题在我的场景中没有那么严重,因为在我开始注意到任何服务器连接延迟时间之前,我能够打开45个连接。即便如此,在连接启动之前也只是几秒钟的事情。但在其他情况下,这可能会很糟糕,所以我重新考虑了我的设计,这将引导我找到当前的解决方案。此解决方案不涉及通过已连接客户机列表枚举以推送数据。这意味着我唯一需要的时间 (或) 同步时钟 )当添加新的AsyncClientConnections或删除不再连接的AsyncClientConnections时,将列出已连接的AsyncClientConnections。换句话说,在推送数据时,传入连接不必等待连接。

    我当前的解决方案有4个组件:

    • AsyncClientConnection:管理异步套接字连接逻辑的类。它负责将数据发送(推送)到连接的客户机。此类包含对Datagetter的引用。
    • pusherserver:管理连接的AsyncClientConnections的类。它接受传入的异步套接字连接,并创建新的异步客户端连接来管理这些套接字。它还删除任何不再连接的异步客户端连接。
    • datagetter:一个用于检索要发送到连接的客户机的数据的类。检索到的数据是图片文件。每次完成图片检索后,Datagetter都会引发一个DataRetrieved事件。
    • TasyncClientEventArgs:从EventArgs继承的类。它用于在引发DataRetrieved事件时传输Datagetter类检索到的数据。

    pusherserver包含一个datagetter实例和一个AsyncClientConnections列表。当一个新的套接字连接建立到pusherserver时,它会创建一个新的AsyncClientConnection,并将一个引用传递给套接字连接和一个对datagetter的引用。

    创建新的AsyncClientConnection时,它指定一个方法,用于处理Datagetter的DataRetrieved事件。

    现在,当Datagetter实例引发DataRetrieved事件时,所有连接的AsyncClientConnections都会将检索到的数据推送到客户端。不需要通过AsyncClientConnections列表进行枚举,推送数据时不需要锁定AsyncClientConnections列表,建立连接比第一个解决方案更快更顺畅。

    希望这能帮助其他面临同样问题的人。

    -弗林尼

        2
  •  0
  •   Doug    15 年前

    解决方案的一个问题是,您将在整个推送操作中持有一个锁。如果这需要很长时间,新客户机将被阻塞,直到发生这种情况。当您增加被推送数据的频率时,这可以有效地阻止新客户机。

    我建议您锁定列表,制作一个副本,释放锁,然后枚举副本以发送数据。这在负载下可能会表现得更好。(请记住,您复制的是列表,而不是列表中的基础对象)