WCF作为Windows服务组件
本文关键字:服务组 组件 服务 Windows 作为 WCF | 更新日期: 2023-09-27 17:52:57
我在这里寻找最佳实践。我正在开发一个Windows服务,它可以主动监控一些事情,并通过数据库进行配置,它还可以写入日志和状态更改等。
现在我想连接GUI作为客户端应用程序来更改配置或在某些事情发生时获得GUI警报。我想使用WCF。
但我不确定如何实现这一点。我可以创建一个WCF服务,我已经有了一些经验,我甚至已经构建了一个回调。但是由于WCF服务使用无状态连接,每次连接后所有与之相关的对象都会丢失,所以我必须将大多数东西声明为静态,这感觉像是一种hack。
另一方面,我可以使用我已经创建的Windows服务,并添加一个WCF组件到它。但是我不明白如何将持久对象从服务连接到wcf。所以基本上我不明白如何构建一个持久运行的服务,它有一个WCF组件(我认为至少是这样)是在连接事件上创建的,之后处理和死亡,但可以访问来自"父"服务的信息/对象。这是可以做到的,还是我在这种情况下过分扭曲和扭曲WCF概念?
编辑:澄清:我可以做无状态连接。但是如何保持回调有效呢?如果我把所有的"智能"放入WFC服务,在有人连接到它之前,它不会运行(在此之前不调用构造函数)。如果我把我的智能放到一个"正常"的windows服务中,我不知道,如何保持回调连接存活,也不知道如何从外部触发WCF服务中的函数。(第二个编辑):这是我今天尝试的。基本上,我将WCF托管在ConsoleApp中(用于测试)。客户端可以连接,然后WCF服务器创建一个回调。托管应用程序本身有对WCF服务的引用,可以将消息推送到WCF服务中。然后,WF本身可以检查是否存在回调,如果存在,则将消息放在列表中,该列表将在线程中处理。当调用host.Open():
时,由于tcp端口上的访问错误,它遵从但不会启动。Der Zugriff auf einen Socket war aufgrund Der Zugriff rechte des Sockets unzulässig) beim Abhören and 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在运行时抱怨端点不知道。也许有人可以看看我的应用程序配置文件:服务器:
<?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的通信异常,尽管异常发生在我启动程序半秒后,没有内部异常,也绝对没有线索发生了什么。我花了两个小时在谷歌上找到了几十个人,他们都得到了例外,几乎每次都是由于其他原因。我改变了maxConnections, maxMessageSize等几次没有任何改变,我不知道,如何调试这个。
我必须说我非常激动,因为这是不可能调试的,我再次觉得我必须扭曲和弯曲WCF,因为我只想要更多的愚蠢的无连接的HTTP事务。任何人请分享如何使用这个。我不可能是第一个有这个要求的人。如果我不能在明天早上进入这个,我将为此实现一个TCP套接字,因为如果我立即开始这样做,现在已经完成了。对不起,我现在很沮丧。
[quit ON THIS]我又花了一天时间调试,但没有成功。由于响应是如此之小,我的结论是,WCF从来没有这样使用过。我觉得WCF只是webrequest的一个花哨的玩具而已。网络上没有我的例子,所以我把它扔掉了。我下周要交一个原型,我不能再玩了。
这是一个很难回答的问题,因为它是如此抽象。
简短的回答是,是的!你可以而且应该在你的Windows服务上实现一个WCF端点,让你的GUI与之对话。
你不需要做任何静态的东西。就连接到持久对象而言,您需要为每个"持久"对象创建可序列化的数据契约,并通过WCF发送和接收这些契约。
我不认为需要两个独立的服务,只是听起来工作量很大。
如果我能提供更多的信息或澄清任何事情,请告诉我!
更新:您的客户端应该调用一个"订阅"函数,该函数将它们的InstanceContext放入订阅列表中,然后您可以调用回调。这样做使连接是有状态的。您可能需要编写自己的代理来完成所有这些工作,生成的代理不能很好地进行双工通信。我还建议使用net:tcp绑定,如果你还没有。
这里有一个双工服务的参考,如果你还没有看到它,MSDN
不太确定最好的答案是什么,但我有一种感觉,你需要阅读SignalR。根据我模糊的记忆,我认为这可以执行推送通知。如果我完全错了,我道歉!!