如何检测已断开的双工轮询客户端

6
我已经跟随Tomek Janczuk的使用HTTP轮询双工WCF通道的发布/订阅示例,但我注意到当客户端关闭浏览器断开连接时,服务在下一次回调时没有发现。 我本来期望会出现异常或类似的东西,以表示终结点不再存在。

如何知道客户端已经离开,以便停止向该客户端发布?


回调方法是“OneWay”的吗?在OperationContract属性上设置IsOneWay为false会使服务器检测超时吗?(我不确定,所以只是评论而不是回答) - Andrew Shepherd
4个回答

5

确切的说: 不可能

当TCP连接被关闭时(作为HTTP调用的基础),会向服务器发送一条特殊的TCP消息-FIN数据包。虽然HTTP是无状态的,但基础的TCP连接是有状态的,并且通过保持活动状态,底层的TCP连接通常保持打开状态。如果客户端被处理,TCP连接将被关闭并通常向服务器发送一条消息。但是,如果它崩溃或其网络断开连接,则它将没有时间执行此操作。所以总之,你永远无法确定。

这里 了解更多信息。


总之,我喜欢这个答案。 :-) - Jason Kleban

3

由于受限的SL双工功能,这几乎是不可能的。我们在服务中实现了用户列表,并添加了“IsDisconnected”和“LastCommunicationTime”属性。当WCF服务尝试向用户的传出消息队列中添加消息时,如果超时并失败,会抛出超时异常。我们将“IsDisconnected”标记为true,并在下次不再尝试向该用户发送消息。

另一个线程一直监视着这个情况,如果它注意到“LastCommunicationTime”已经超过了一定时间,并且“IsDisconnected”为true,则会将该用户从列表中移除,除非同一用户在此期间内再次尝试连接(我们通过其UserId来识别)。

由于这个问题使WCF服务非常繁忙,因此我们手动处理了很多事情。


我最终采用了一个非常相似的模式。 - Ralph Shillington
但请记住:这会带来另一个问题,服务器现在太聪明了,随时准备踢出用户 :), 那些没有离开但非常沉默的用户怎么办?但是他们的“建立连接调用失败了”,他们的客户端通道已经出现故障,在这种情况下,他们将不得不再次打开通道,这将为他们提供一个新的会话ID,并且该通道将是新的。因此,在再次注册服务器时,服务器应从列表中删除旧的用户项目并添加一个新的/或使用新的更新它(ClientCallback Channel在其中非常重要)。 - Mazhar Karimi
当客户端的通道因“建立连接请求失败”而出现故障时,服务器不会立即知道。并且当服务器池超时时,客户端通道故障会在服务器上触发。这是WCF服务的“盲动作”,如果你知道我的意思的话。虽然我对于那种情况下的serverPooltimeout不确定100%,老实说,我觉得它是一种随机时间。 - Mazhar Karimi

3
似乎有一个不太令人满意的简单解决方案:如果客户端的回调超时了,则不再调用它。
在我的系统中,我还实现了一个手动的“检查”调用 - 每隔n秒,服务器会通过回调通道为每个注册的客户端调用一个无参数方法,以查看客户端是否仍在运行。我开始怀疑这是否真的是个好主意 - 我遇到了一个新问题,即由于我在调试器中暂停了客户端,所以一直出现回调超时。

在我的初始测试中,问题是我没有得到超时。我正在尝试一种类似于你的方法,只不过是相反的。客户端“ping”服务器,如果服务器在一定时间内没有收到它们的消息,则它们将从列表中删除,这与asp.net会话管理类似。 - Ralph Shillington
@Ralph - 我正在使用 netTcpBinding(它是一个厚客户端,而不是 Silverlight)。也许 PollingDuplexHttp 绑定只是不会超时,如果是这样,你所描述的解决方案将是唯一的选择。 - Andrew Shepherd

1
我遇到了这个问题,并创建了一个线程,使用以下代码删除已断开连接的客户端。它可以正常工作,但在10-15分钟后会将已断开连接的客户端从客户端列表中删除(对我来说还好)。
    new Thread(new ThreadStart(() =>
    {
        while (SilverlightClients != null)
        {
                lock (SilverlightClients)
                {
                    SilverlightClients = SilverlightClients.Where(d => (d.Callback as IContextChannel).State != CommunicationState.Opened).ToList();
                }

            Thread.Sleep(1000);
        }
    })) { Name = "Thread Remove Disconnected Clients" }.Start();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接