我在这里寻找最佳实践。 我正在开发一个Windows服务,它主动监视多个事情,并通过数据库进行配置,它还会写入日志和状态更改等。
现在我想将GUI连接为客户端应用程序以更改配置或在发生某些事情时提醒GUI。我想过为此使用WCF。
但我不知道如何实现这一点。我可以创建一个WCF服务,我已经有了一些经验,我甚至已经构建了一个回调。但是由于WCF服务使用无状态连接,所有与之相关的对象在每次连接后都会丢失,因此我必须声明大多数静态的东西,这些东西感觉像是黑客。
另一方面,我可以使用我已经创建的Windows服务并向其添加WCF组件。但我无法理解如何将持久对象从服务连接到wcf。
所以基本上我不明白如何构建一个持久运行的服务,它有一个WCF组件(这就是我认为至少)我是在连接事件,进程和死之后创建的,但是可以访问信息/对象来自'父母'服务。可以这样做,还是在这种情况下弯曲和扭曲WCF概念太多?
编辑:要进行clerify:我可以进行无状态连接。但是如何让回调保持活力?如果我将所有“智能”放入WFC服务中,则在有人连接到它之前它没有运行(之前没有调用构造函数)。如果我把我的智能放到'正常'的Windows服务中,我不知道,如何保持calback连接存活,我也不知道如何从外部触发WCF服务中的功能。
[第二编辑]: 这就是我今天所尝试的。基本上我在ConsoleApp中托管WCF(用于测试)。客户端可以连接,然后WCF服务器创建回调。托管应用程序本身具有对WCF服务的引用,可以将消息推入其中。然后,WF本身可以检查是否存在回调,如果存在,则将消息放入列表中,该消息在线程中处理。 它符合但由于在调用host.Open()时tcp端口上的访问错误而无法启动:
Der Zugriff auf einen Socket war aufgrund der Zugriffsrechte desSocketsunzulässig)beimAbhören,IP-Endpunkt = 0.0.0.0:8732。
Google翻译的英文翻译:
通过套接字的访问权限侦听IP端点时,
对套接字的访问无效)
到目前为止,这是我的来源: 服务接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFHostInfo
{
[ServiceContract]
public interface IWCFService
{
[OperationContract]
bool informClient(IpEvent value);
[OperationContract]
bool connectClient();
[OperationContract]
bool disconnectClient();
}
public interface IClientCallback
{
[OperationContract]
void onCallback(IpEvent value);
}
[DataContract]
public class IpEvent
{
String _ip;
DateTime _time;
[DataMember]
public String ip
{
get { return _ip; }
set { _ip = value; }
}
[DataMember]
public DateTime time
{
get { return _time; }
set { _time = value; }
}
}
}
这是服务:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
namespace WCFHostInfo
{
[ServiceBehavior (ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class WCFService : IWCFService
{
public static IClientCallback callback;
private static Object queueLock = new Object();
private static bool thredStop;
private Thread queueThread;
private static List <IpEvent> iplist = new List <IpEvent>();
public WCFService()
{
}
public bool disconnectClient()
{
thredStop = true;
queueThread.Join();
callback = null;
return true;
}
public bool informClient(IpEvent value)
{
if (callback != null)
{
lock (queueLock)
{
iplist.Add(value);
}
return true;
}
return false;
}
private void pushClient()
{
while (!thredStop)
{
bool sleep = true;
lock (queueLock)
{
if (iplist.Count != 0)
{
sleep = false;
}
}
if (sleep)
{
Thread.Sleep(250);
}
else
{
List<IpEvent> worklist = new List<IpEvent>();
lock (queueLock)
{
worklist = iplist;
iplist = new List<IpEvent>();
}
foreach (IpEvent item in worklist)
{
callback.onCallback(item);
}
}
}
}
public bool connectClient()
{
callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
queueThread = new Thread(pushClient);
thredStop = false;
queueThread.Start();
return true;
}
}
}
这是托管控制台应用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WCFHostInfo;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.Threading;
using TestKonsole.ServiceReference1;
namespace TestKonsole
{
class Program
{
static WCFServiceClient client;
static bool stopThread;
static void Main(string[] args)
{
client = new WCFServiceClient();
stopThread = false;
ServiceHost myhost = new ServiceHost(typeof(WCFHostInfo.WCFService));
NetTcpBinding netTcp = new NetTcpBinding();
netTcp.Security.Mode = SecurityMode.None;
netTcp.Security.Message.ClientCredentialType = MessageCredentialType.None;
netTcp.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
netTcp.MaxReceivedMessageSize = 2147483647;
myhost.AddServiceEndpoint(typeof(WCFHostInfo.IWCFService), netTcp, "net.tcp://localhost:8732/serv");
ServiceMetadataBehavior beh = new ServiceMetadataBehavior();
myhost.Description.Behaviors.Add(beh);
Thread pushThread = new Thread(push);
myhost.Open();
//This is where the exception occures...
Console.WriteLine("Host opened");
Console.ReadLine();
stopThread = true;
pushThread.Join();
myhost.Close();
Console.WriteLine("Host closed");
Console.ReadLine();
}
private static void push()
{
while (!stopThread)
{
Thread.Sleep(5000);
IpEvent ev = new IpEvent();
ev.ip = "192.156.45.9";
ev.time = DateTime.Now;
bool res = client.informClient(ev);
if (res)
{
Console.WriteLine("Client informed!");
}
else
{
Console.WriteLine("No client connected.");
}
}
}
}
}
[编辑3] 我现在可以设法在托管应用程序中创建服务引用。但我无法发送任何内容,因为Visual Studio在运行时抱怨端点未知。也许有人可以查看我的app.config文件: 服务器:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="PaewemaZeitWatcher.Service1Behavior"
name="WCFHostInfo.WCFService">
<endpoint address="" binding="netTcpBinding" contract="WCFHostInfo.IWCFService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8732/TestWCF/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="PaewemaZeitWatcher.Service1Behavior">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
客户端:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IWCFService" />
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8732/TestWCF/
"binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IWCFService"
contract="ServiceReference1.IWCFService"
name="NetTcpBinding_IWCFService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
老实说,我不能再看透这个了,因为我认为我的需求是一个非常普遍的(现有的独立服务,可以附加一个gui,接收电话),但我似乎是第一个实现这一点的人。
[最后编辑] 我从托管应用中删除了这些行
netTcp.Security.Mode = SecurityMode.None;
netTcp.Security.Message.ClientCredentialType = MessageCredentialType.None;
netTcp.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
netTcp.MaxReceivedMessageSize = 2147483647;
现在我甚至可以从托管应用程序连接到WCF,但我不能同时处理两个连接。当我的实际客户端尝试连接时,我得到一个完全无理的超时值00:00:59.999的通信异常,尽管异常发生在我启动程序后半秒钟,没有内部异常且绝对没有CLUE发生了什么。 我花了最近2个小时在Google上找到了几十个人,他们都得到了例外,几乎是因为另一个原因。我多次更改maxConnections,maxMessageSize等没有任何更改,我没有看到,如何调试它。
我必须说我非常激动,因为这是不可能调试的,而且我不得不扭曲和弯曲WCF,因为我只想要一个愚蠢的无连接HTTP事务。任何人请分享如何使用它。我不能成为第一个有这个要求的人。 如果我明天早上无法进入此状态,我将为此实现一个TCP套接字,因为如果我马上开始这样做的话,现在已经完成了。 对不起,现在感到非常沮丧。
[退出此] 我花了一天时间调试没有成功。由于响应非常小,我的结论是,WCF从未以这种方式使用。 我觉得WCF对Webrequests来说是一个奇特的玩具。 网络上的任何示例都没有我的用例,所以我将其转储。下周必须提供原型,我不能再玩了。
答案 0 :(得分:0)
这是一个难以回答的问题,因为它是如此抽象。
简短的回答是,是的!您可以而且应该在Windows服务上实现WCF端点,以便与GUI进行通信。
你不需要做任何静态的事情。就连接到持久对象而言,您需要为每个“持久”对象创建可序列化的数据协定,并通过WCF发送和接收这些协议。
我认为不需要两个单独的服务,听起来像是很多工作。
如果我能提供更多信息或澄清任何内容,请告诉我们。
更新: 您的客户端应调用“订阅”功能,将其InstanceContext放入订阅列表,然后您可以调用回调。这样做会使连接有状态。您可能需要编写自己的代理才能完成所有这些工作,生成的代理不能很好地完成双工通信。我还建议使用net:tcp绑定,如果你还没有。
以下是双工服务的参考资料,如果您还没有看到,MSDN
答案 1 :(得分:-1)
不太确定最佳答案是什么,但我觉得你需要阅读SignalR。从我模糊的回忆中我认为这可以执行推送通知(不知何故)。抱歉,如果我完全错了!!