简体   繁体   中英

WCF as a Windows Service component

I am looking for best practices here. I am developing a Windows Service, which is actively monitoring several things and is configured through a database where it also writes logs and status changes and such.

Now I want to connect a GUI as a client application to change configuration or to get the GUI alerted when certain things occur. I thought of using WCF for this.

But I am not sure how to implement this. I can create a WCF Service, I have already some experience with that, I even build a callback already. But as the WCF Service uses stateless connection, all objects concerning it are lost after each connection, so I would have to declare most things static which feels like a hack.

On the other hand I could use my already created Windows Service and add a WCF component to it. But I fail to understand how to connect the persistent objects from the service to the wcf.

So basically I dont understand how to build a peristant running Service, which has a WCF component which (thats how I think at least) is created on a connection event, processes and dies after that, but has access to information/objects from the 'parent' Service. Can this be done or do I bend and twist the WCF concept too much in this case?

Edit: To clerify: I can do stateless connections. But how to keep the callback alive? If i put all 'intelligence' into the WFC Service, it is not running before someone connects to it (the constructor is not called before that). If I i put my intelligence into a 'normal' windows service, I do not know, how to keep the calback connection alive nor do I know how to trigger functions within the WCF service from outside.

[SECOND EDIT]: This is what I tried today. Basically I host the WCF in a ConsoleApp (for testing). A Client can connect and the WCF Server creates a Callback then. The hosting application itself has a reference to the WCF service and can push Messages into it. The WF itself can then check if there is a callback present, and if so puts the message on list, which is processed in a thread. It complies but wont start due to an accessing error on the tcp port when calling host.Open():

Der Zugriff auf einen Socket war aufgrund der Zugriffsrechte des Sockets unzulässig) beim Abhören an IP-Endpunkt=0.0.0.0:8732.

English translation by Google Translate:

Access to a socket was invalid) when listening on IP Endpoint by its access permissions of the socket

This is my source so far: Service Interface:

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; }
        }
    }        
}

This is the service:

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;
        }
    }

}

This is the hosting console app:

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.");
                }
            }
        }
    }
}

[EDIT 3] I can manage to create a service reference in the hosting app now. But i cant send anything because Visual Studio complains at runtime that the endpoint is not known. Perhaps someone cann have a look at my app.config file: Server:

<?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>

Client:

<?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>

I honestly cant see through this any more because I think my need is a quite common one (existing stand alone service, where one can attach a gui to, which receives calls) but I seem the first one ever to implement this.

[LAST EDIT] I removed this lines from the hosting app

netTcp.Security.Mode = SecurityMode.None;
netTcp.Security.Message.ClientCredentialType = MessageCredentialType.None;
netTcp.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
netTcp.MaxReceivedMessageSize = 2147483647;

Now I can connect to the WCF even from the hosting application but I cant handle two connections at once. When my actual client tries to connect I get a communication exception with a completely irrational timeout value of 00:00:59.999 although the Exception occurs half a second after I start the program, with no inner exception and ABSOLUTELY NO CLUE on what has happened. I spend the last 2 hours finding dozens of people on Google who all got the exception, almost ever time due to another cause. I changed maxConnections, maxMessageSize etc. several times without any change and I don't see, how to debug this.

I must say I am deeply agitated, because this is impossible to debug, and again I fell I have to twist and bend WCF because I just want more then a stupid connectionless HTTP transaction. Anybody PLEASE SHARE on how to use this. I cant be the first one with this requirement. If I can't get into this by tomorrow morning I will implement a TCP socket for this BECAUSE THIS WOULD ALREADY BE DONE by now if I had started that way right away. Sorry for being deeply frustrated right now.

[QUITTING ON THIS] I spend another day debugging without success. Since the responses are so small my conclusion is, that WCF is never used this way. I get the feeling WCF is a fancy toy for Webrequests nothing more. No example on the web features my usecase so I dump it. Have to deliver a prototype next week and i cant play around any longer.

This is a hard question to answer, since it is so abstract.

The short answer is, Yes! You can and should implement a WCF endpoint on your Windows service for your GUI to talk to.

You shouldn't need to make anything static. As far as connecting to your persistent objects, you will need to create serializable data contracts for each of your "persistent" objects, and send and receive those via WCF.

I don't see a need for two separate services, just sounds like a lot of work.

Let me know if I can provide any more information or clarify anything!

UPDATE: Your clients should be calling a "Subscribe" function that puts their InstanceContext into a subscription list, that you can then call the callbacks on. Doing this makes the connection stateful. You may need to write your own proxy to make all this work, the generated ones don't do duplex comms very well. I would also recommend using the net:tcp binding if you arent' already.

Here is a reference for duplex services if you haven't seen it already, MSDN

Not quite sure what the best answer is, but I've a feeling that you need to read up on SignalR. From my vague recollection I think this can perform push notifications (somehow). Apologies if I'm completely wrong!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM