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

时间:2009-07-17 21:01:30

标签: .net sockets

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

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

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

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

我的问题在于通用列表,它不是线程安全的,并且在这种异步场景中运行不佳。当新套接字连接到服务器时,会为其创建一个新的AsyncClientConnection并添加到通用列表中;但是如果服务器正在将文件推送到连接的客户端,则它使用通用列表,因此当新的AsyncClientConnection添加到列表中时,列表会被修改,并且由于显而易见的原因会发生异常。

我之前从未认真地使用过线程(我只是尝试过它们以确保我理解它们的理论),并且之前从未使用过异步套接字,所以当它到来时我有点迷失我可以用来解决这个问题的工具。

我甚至质疑是否有不同的方法来维护AsyncClientConnection类型。

任何建议都会很棒!

非常感谢,

-Frinny

2 个答案:

答案 0 :(得分:1)

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

在C#中,我将使用的关键字是 lock ;因为我正在使用VB.NET,所以我使用了 SyncLock 关键字。 lock SyncLock 关键字可防止多个线程访问线程资源。只要每段代码在使用之前锁定资源,就可以正常工作。

此解决方案有效;但是,正如Doug指出的那样,新连接必须等待服务器完成将数据推送到连接的客户端。随着服务器推送数据的频率增加,连接客户端的数量增加,我开始注意到新客户端需要一段时间才能连接。

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

我目前的解决方案有4个组成部分:

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

PusherServer包含DataGetter的实例和AsyncClientConnections列表。当与PusherServer建立新的套接字连接时,它会创建一个新的AsyncClientConnection并向其传递对套接字连接的引用和对DataGetter的引用。

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

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

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

-Frinny

答案 1 :(得分:0)

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

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