从服务关闭 WCF 连接

Closing WCF Connections from service

我最近开始了一份使用 WCF 服务的新工作。我过去使用过它们并且对它们很满意,但据我所知,如果客户端不关闭连接,它有能力完全关闭您的服务。我知道关闭连接的正确程序,但如果责任在客户端,他们可能不会遵循相同的做法,并可能有能力关闭服务。是否有任何其他方法来处理连接的关闭,以便它不依赖于客户端做正确的事情?任何有权访问您的服务的人都能如此轻松地将其关闭似乎很奇怪...

非常感谢您的任何见解!

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

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

1)客户端关闭代理

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

3) 服务抛出一个非故障异常,这将使通道出现故障并因此终止会话 如果您不想让客户参与进来,那么您只有 2 和 3,这两种情况都不适合客户 - 在下一次尝试与服务对话时,他们在这两种情况下都会遇到异常。

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


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

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

a: 使用静态字典保存Client的IP和回调通道。在共享对象上写入之前,锁定对象。

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

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

如果您使用的是名称管道绑定,您将不会获得 RemoteEndpointMessageProperty

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

d: 当 WCF 服务的用户想要断开客户端连接时,he/she 将使用客户端的 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; }
}