从服务关闭 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; }
}
我最近开始了一份使用 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; }
}