2016-07-15 47 views
1

我正在开发使用WPF和WCF的客户端/服务器应用程序。 服务器应用程序托管一个WCF服务,负责执行客户端请求并在出现问题时回调它们。在忙于长时间运行的操作时Ping应答WCF服务

服务接口与所有的OneWay操作定义一个双工回调合同。

(简体)IService

[ServiceContract(CallbackContract = typeof(ISrvServiceCallback))] 
public interface ISrvService 
{ 
    [OperationContract(IsOneWay = true)] 
    void Ping(); 

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

public interface ISrvServiceCallback 
{ 
    [OperationContract(IsOneWay = true)] 
    void PingReply(); 

    [OperationContract(IsOneWay = true)] 
    void LongRunningOperationStatus(string reply); 
} 

的服务需要十个分量某些对象根据客户端的调用改变状态。出于这个原因,我决定有一个单身人士的服务。

(简体)服务

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class SrvService : ISrvService 
{ 
    MyObject statusObject; 

    public void LongRunningOperation() 
    { 
     //calling back the client reporting operation status 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>() 
     .LongRunningOperationStatus("starting long running application"); 

     statusObject.elaborateStatus(); 

     //calling back the client reporting object status 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>() 
     .LongRunningOperationStatus("object status: " + statusObject.ToString()); 
    } 

    public void Ping() 
    { 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>().PingReply(); 
    } 


    public SrvService() 
    { 
     statusObject= ...statusObject init... 
    } 

} 

正如你可以看到我有一个操作由服务暴露了一个客户端调用(每5秒),以检查是否在服务器应用程序上可用网络(每个客户端有一个服务器连接图标红色 =服务器不可用,绿色 =服务器不可用)。

当客户端请求长时间运行的操作,启动服务器上运行的工作,不能回复ping请求(客户端的服务器连接图标变为红色)。

一旦长时间运行的操作完成时,服务器回应所有由客户提出的要求和服务器连接图标重新变绿)。

我想找到一种方法来开发服务,以便服务器始终回应ping请求,也是在长时间运行时。

  • 我怎么能做到这一点考虑,我需要保持 InstanceContextMode.Single到十个分量的 服务对象的状态?
  • 是否有其他/更好的方法来ping WCF服务 可用性和可视化显示客户端上的结果?

回答

1

对于单例服务,您将需要多线程实现您的服务器实例以获得所需的行为,至少您需要在单独的线程上运行LongRunningOperation。如果这个操作本质上不是线程安全的,那么在实现中需要特别注意锁定或信号量等多重并发调用。当客户端调用LongRunningOperation()时,这种方式会在单独的线程中执行,并可以自由地响应ping请求。

有很多方法可以实现这一点。顺便提一句,你的问题似乎是客户端正在进行异步调用(因为它似乎在等待LongRunningOperation返回时发出ping请求) - 所以我也假设你有一些异步编程的知识。WCF有一些built in ways of handling concurrency,但大多数文档不包含单例实例,因此您需要仔细阅读并专注于特殊情况。

我已经与异步/ AWAIT模式最成功的(见herehere) - 一旦被正确设置我有一个状态单独服务长期运行的服务电话一个非常可靠和可预测的模式。另外,就ping而言,你应该指出你只是显示用户的连接状态,但是如果你有计划使用它来进行控制(在打电话之前检查服务是否在线) there is a lot of discussions here on why you should avoid it.

编辑:与异步快速示例/等待

[ServiceContract] 
public interface ISrvService() 
{ 
    [OperationContract] 
    bool Ping(); // doesnt need to be async 

    [OperationContract] 
    Task<string> LongRunningOperation(); 
} 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class SrvService : ISrvService 
{ 
    MyObject statusObject; 

    public async Task LongRunningOperation() 
    { 
     // lock/semaphore here if needed 
     await Task.Run(() => statusObject.elaborateStatus()); // you could impliment elaborateStatus() as an async Task method and call it without Task.Run 
     return statusObject.ToString(); 
    } 

    public bool Ping() 
    { 
     return true; 
    } 


    public SrvService() 
    { 
     statusObject= ...statusObject init... 
    } 

} 
public class SrvClient : ClientBase<ISrvService> 
{ 
    public async Task<string> LongRunningOperation() 
    { 
     return await base.Channel.LongRunningOperation(); 
    } 

    public async Task<bool> Ping() 
    { 
     // note that we still call this with an await. In the client we are awaiting the wcf service call 
     // this is independent of any async/await that happens on the server 
     return await Task.Run(() => base.Channel.Ping()); 
    } 
} 

使用客户端:

public class SomeApplicationClass() 
{ 
    SrvClient Client; 
    DispatcherTimer PingTimer; 

    public SomeClass() 
    { 
     BasicHttpBinding binding = new BasicHttpBinding(); 
     EndpointAddress endpoint = new EndpointAddress(
      "http://...:8000/Service/Address"); 
     OutpostClient = new OutpostRemoteClient(binding, endpoint); 

     // pingTimer setup 

    } 

    // async voids are scary, make sure you handle exceptions inside this function 
    public async void PingTimer_Tick() 
    { 
     try 
     { 
      await Client.Ping(); 
      // ping succeeded, do stuff 
     } 
     catch // specify exceptions here 
     { 
      // ping failed, do stuff 
     } 
    } 

    public async Task DoTheLongRunningOperation() 
    { 
     // set busy variables here etc. 
     string response = await Client.LongRunningOperation(); 
     // handle the response status here 
    } 
} 

而且this answer似乎有关。

+0

谢谢你的回复@ plast1k!我对异步知识很少,而且我实际上也在探索这个领域。 客户端使用调用_ThreadPool.QueueUserWorkItem(checkService)_的_DispatcherTimer_来ping服务。 _checkService_调用_serviceClient.Ping()_。 我会看看你发布的所有链接,看看我能如何解决我的问题。 是否有可能有你的异步/等待单例服务实现的一些示例代码?只是想知道如何从客户端调用并从服务器回复。 – aDone

+0

没问题。我应该能够稍后再挖掘一些,如果我找到它,我会编辑我的帖子。 – plast1k

+0

@aDone示例添加,对不起,花了这么久! – plast1k