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的一个花哨的玩具而已。网络上没有我的例子,所以我把它扔掉了。我下周要交一个原型,我不能再玩了。

WCF作为Windows服务组件

这是一个很难回答的问题,因为它是如此抽象。

简短的回答是,是的!你可以而且应该在你的Windows服务上实现一个WCF端点,让你的GUI与之对话。

你不需要做任何静态的东西。就连接到持久对象而言,您需要为每个"持久"对象创建可序列化的数据契约,并通过WCF发送和接收这些契约。

我不认为需要两个独立的服务,只是听起来工作量很大。

如果我能提供更多的信息或澄清任何事情,请告诉我!

更新:您的客户端应该调用一个"订阅"函数,该函数将它们的InstanceContext放入订阅列表中,然后您可以调用回调。这样做使连接是有状态的。您可能需要编写自己的代理来完成所有这些工作,生成的代理不能很好地进行双工通信。我还建议使用net:tcp绑定,如果你还没有。

这里有一个双工服务的参考,如果你还没有看到它,MSDN

不太确定最好的答案是什么,但我有一种感觉,你需要阅读SignalR。根据我模糊的记忆,我认为这可以执行推送通知。如果我完全错了,我道歉!!