WCF作为Windows服务组件

时间:2014-03-25 13:38:39

标签: c# wcf service ipc

我在这里寻找最佳实践。 我正在开发一个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来说是一个奇特的玩具。 网络上的任何示例都没有我的用例,所以我将其转储。下周必须提供原型,我不能再玩了。

2 个答案:

答案 0 :(得分:0)

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

简短的回答是,是的!您可以而且应该在Windows服务上实现WCF端点,以便与GUI进行通信。

你不需要做任何静态的事情。就连接到持久对象而言,您需要为每个“持久”对象创建可序列化的数据协定,并通过WCF发送和接收这些协议。

我认为不需要两个单独的服务,听起来像是很多工作。

如果我能提供更多信息或澄清任何内容,请告诉我们。

更新: 您的客户端应调用“订阅”功能,将其InstanceContext放入订阅列表,然后您可以调用回调。这样做会使连接有状态。您可能需要编写自己的代理才能完成所有这些工作,生成的代理不能很好地完成双工通信。我还建议使用net:tcp绑定,如果你还没有。

以下是双工服务的参考资料,如果您还没有看到,MSDN

答案 1 :(得分:-1)

不太确定最佳答案是什么,但我觉得你需要阅读SignalR。从我模糊的回忆中我认为这可以执行推送通知(不知何故)。抱歉,如果我完全错了!!