从服务关闭WCF连接

时间:2015-03-06 17:21:12

标签: wcf

我最近开始了一项使用WCF服务的新工作。我过去曾经使用它们并且对它们很满意但是从我记忆中如果客户端没有关闭连接它就能够完全降低你的服务。我知道关闭连接的正确程序,但如果责任在客户端,他们可能不遵循相同的做法,并且可能有能力降低服务。有没有其他方法来处理关闭连接,以便它不依赖于客户做正确的事情?任何能够访问您服务的人都能够轻松地将其降级​​......这似乎很奇怪......

非常感谢您的任何见解!

1 个答案:

答案 0 :(得分:1)

  • 一种选择是在服务器中使用会话超时。这实际上会破坏客户端通道。

会话终止的方式实际上只有三种:

1)客户关闭代理

2)在客户端发送另一个请求之前,超过了服务的receiveTimeout

3)服务抛出一个非故障异常,这会使通道出现故障,从而终止会话 如果您不想让客户参与,那么您只有2和3两者都不适合客户端 - 他们将在下次尝试与服务交谈时获得例外情况。

您可以使用Duplex messaging并获取服务以通知客户端其需要会话终止 - 然后客户端有机会优雅地关闭代理,但这是一种合作策略


  • 或者您需要使用双工(但客户端仍需要调用该服务)。

以下是服务实施的一些重点:

a:使用静态字典保留客户端的IP和回调通道。在写入共享对象之前,请锁定对象。

b:使用GetAddressAsString方法获取客户端的IP地址。您可以从传入消息中获取客户端的IP。以下语句显示了如何在WCF中获取客户端的IP地址:

RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;

如果您使用的是namepipe绑定,则不会获得RemoteEndpointMessageProperty

c:当客户端创建服务的代理时,它会立即调用StartingService方法。在StartingService方法中,我将客户端和当前实例的回调通道保留在字典中。

d:当WCF服务的用户想要断开客户端时,他/她将使用客户端的IP地址调用Disconnect方法。

e: Disconnect方法使用IP地址获取客户端的回调通道,并从字典中关联客户端的服务实例。最终,它通过使用回叫通道通知客户端并关闭传入通道。

这是通过代码实现的:

[ServiceContract(CallbackContract=typeof(INotifyClientCallback),SessionMode=SessionMode.Required)]
public interface IService1
{

    [OperationContract]
    bool StartingService();
}

public interface INotifyClientCallback
{

    [OperationContract(IsOneWay = true)]
    void Disconnecting();
}

用于回调的INotifyClientCallback接口。

第2步:联系人的实施:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{

    private static readonly Dictionary subscribers = new Dictionary();
    public static event EventHandler onClientAdded;

    ///
    /// Returns the IP Address of the Client
    ///
    ///
    public string GetAddressAsString()
    {

        if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
        {
            return "127.0.0.1";
        }
        RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
        return clientEndpoint.Address;
    }

    public bool StartingService()
    {

        //Get the callback reference
        INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel();
        string IPAddress = GetAddressAsString();

        lock (subscribers)
        {
            if (!subscribers.ContainsKey(IPAddress))
            {
                subscribers[IPAddress] = new CommunicationStore() 
                    { NotifyCallback = callback, 
                      IService = OperationContext.Current.InstanceContext 
                    };
                if (onClientAdded != null)
                {
                    onClientAdded(IPAddress, null);
                }
            }
        }
        return true;
    }

    public static void Disconnect(string ipAddress)
    { 

        if (subscribers.ContainsKey(ipAddress))
        {
            CommunicationStore com = subscribers[ipAddress];

            if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
            {
                try
                {
                    //fires the callback method
                    com.NotifyCallback.Disconnecting();
                    com.IService.IncomingChannels.FirstOrDefault().Close();
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }
    }
}

public class CommunicationStore
{

    public InstanceContext IService { get; set; }
    public INotifyClientCallback NotifyCallback { get; set; }
}