Context:
I need to develop a monitoring server that monitors some of our applications (these applications are in c#). So I decided to develop the system with WCF which seems suitable for my needs.
These applications must register themselves to the monitoring server when they start. After that the monitoring server can call the methods Start or Stop of these applications.
Everything is completely executed on the same machine, nothing needs to be executed remotely.
So I developed a good prototype and everything works fine. Each application registers itself to the monitoring server.
Question:
ApplicationRegistrationService (see the code below) is the implementation of the monitoring service and it is a singleton instance due to the ServiceBehavior attribute.
Here my problem: I want to access the content of ApplicationRegistrationService per example, the number of connected applications from my server (ConsoleMonitoringServer in the example). But, I am not sure how to achieve this.
Do I need to create a channel in my server to the service like I did in my clients (ConsoleClient) or it exists a better way to achieve this?
Code:
The code is very simplified for the purpose of this question:
//The callback contract interface
public interface IApplicationAction
{
[OperationContract(IsOneWay = true)]
void Stop();
[OperationContract(IsOneWay = true)]
void Start();
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IApplicationAction))]
public interface IApplicationRegistration
{
[OperationContract]
void Register(Guid guid, string name);
[OperationContract]
void Unregister(Guid guid);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ApplicationRegistrationService : IApplicationRegistration
{
//IApplicationRegistration Implementation
}
public class ApplicationAction : IApplicationAction
{
//IApplicationAction Implementation
}
Console application for this example
class ConsoleClient
{
static void Main(string[] args)
{
ApplicationAction actions = new ApplicationAction();
DuplexChannelFactory<IApplicationRegistration> appRegPipeFactory =
new DuplexChannelFactory<IApplicationRegistration>(actions,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/AppReg"));
IApplicationRegistration proxy = appRegPipeFactory.CreateChannel();
proxy.Register(Guid.Empty, "ThisClientName");
//Do stuffs
}
}
Console server for this example
class ConsoleMonitoringServer
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
{
host.AddServiceEndpoint(typeof(IApplicationRegistration),
new NetNamedPipeBinding(), "AppReg");
host.Open();
//Wait until some write something in the console
Console.ReadLine();
host.Close();
}
}
}
Finally, I find the answer and it was pretty easy. I just need to create the service instance and pass the reference to the constructor of ServiceHost.
So I need to replace the following code:
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
by :
ApplicationRegistrationService myService = new ApplicationRegistrationService();
using (ServiceHost host = new ServiceHost(myService,
new Uri[]{ new Uri("net.pipe://localhost")}))
If you mean you'd like two way communication between your monitoring service and your registered services or nodes, then you probably should be using two way communication in WCF also known as duplex communication. Very cool stuff.
http://www.codeproject.com/KB/WCF/WCF_Duplex_UI_Threads.aspx
Related
this is my first approach to WCF and named pipes.
What I have to do is a windows service listening on a named pipe while a small GUI tells it what to do through the pipe.
Everything works well: calls are made to the service, responses are delivered to the GUI and the job gets done.
But if I send two concurrent requests from the GUI then the service will process them one by one: I would like to manually manage concurrency service-side and let it run both requests at the same time.
I've tried creating 2 different pipes for the 2 requests and it does what I need, but of course it's not a solution.
I'm using .NET Framework 4.0 and I can't change it.
Here's my example code:
SERVICE: pipe configuration
ServiceHost host = new ServiceHost(typeof(CommandReceiver), new Uri[] {new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(ICommandReceiver), new NetNamedPipeBinding(), "myPipe");
host.Open();
SERVICE: Contract interface and implementation
[ServiceContract]
public interface ICommandReceiver
{
[OperationContract]
string Foo();
[OperationContract]
string Bar();
}
public class CommandReceiver : ICommandReceiver
{
public string Foo()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "FOO";
}
public string Bar()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "BAR";
}
}
CLIENT: pipe configuration
ChannelFactory<ICommandReceiver> pipeFactory = new ChannelFactory<ICommandReceiver>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/myPipe"));
ICommandReceiver serviceProxy = pipeFactory.CreateChannel();
CLIENT: call to the service
public string GetFoo()
{
return serviceProxy.Foo();
}
public string GetBar()
{
return serviceProxy.Bar();
}
Any advice on how to improve the whole thing, even switching to another communication method, would be really appreciate.
Thank you very much in advance!
OK, I've solved this: first of all I was missing the following attribute on my contract implementation class:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class CommandReceiver : ICommandReceiver
{
public string Foo()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "FOO";
}
public string Bar()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "BAR";
}
}
And then I've discovered that the service works perfectly, but not while debugging in visual studio. Releasing and installing it on the system solved the issue.
I have an EXE that I've created called logger which is a simple WinForms application. It has a richtextbox and that's pretty much it.
I then also have a suite of other applications. What I want to be able to do with these apps is to be able to get them to write output to the logger.exe I have full control over the code of all applications.
I understand I could do a process.start and specify arguments but I want these apps to be able to write to the richtextbox at will based on the methods being called within them.
I was hoping I could create an api in the logger.exe that would expose a method for appending the richtextbox.
Does anyone have any tips on how I might achieve this?
EDIT: This is what I have so far:
namespace ScreenLog
{
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger : Form, IFromClientToServerMessages
{
public Logger()
{
InitializeComponent();
}
public void DisplayTextOnServerAsFromThisClient(string text)
{
LogConsole.AppendText(Environment.NewLine + text);
}
}
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
}
As you might have already guessed you would need any of IPC(Inter Process Communication) mechanism to send messages between different processes(Applications). WCF is one of the option, You could implement a simple WCF service module which uses net.pipe binding. This service can be hosted in managed application. In your case this service can be hosted in your logger application.
Note: If you want to host a WCF application in a managed application, Particular managed application(Logger) should have admin privilege.
Implementation of Logger Form
partial class declaration
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger: Form, IFromClientToServerMessages
Introduce Interface for communication
This interface should be added to a assembly which is accessible by both Logger application and any other application which sends message to logger.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
Implementing Interface
Add the following method implementation to your Logger form
public void DisplayTextOnServerAsFromThisClient(string text)
{
//Add proper logic to set value to rich text box control.
richtextbox = text;
}
Hosting the WCF service in logger application
Invoke HostTheNetPipeService() within the constructor of Logger Form
private void HostTheNetPipeService()
{
serverHost = new ServiceHost(this);
serverHost.AddServiceEndpoint((typeof(IFromClientToServerMessages)), new NetNamedPipeBinding(), "net.pipe://127.0.0.1/Server");
serverHost.Open();
}
Call the service from other applications to send message/text
private void SendMessageToLogger()
{
using (ChannelFactory<IFromClientToServerMessages> factory = new ChannelFactory<IFromClientToServerMessages>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Server")))
{
IFromClientToServerMessages clientToServerChannel = factory.CreateChannel();
try
{
clientToServerChannel.DisplayTextOnServerAsFromThisClient("Message to be displayed");
}
catch (Exception ex)
{
}
finally
{
CloseChannel((ICommunicationObject)clientToServerChannel);
}
}
}
Closing the communication channel
private void CloseChannel(ICommunicationObject channel)
{
try
{
channel.Close();
}
catch (Exception ex)
{
}
finally
{
channel.Abort();
}
}
I'm looking for a way to cache objects in memory with a RESTful WCF service. The service is completely stateless and is hosted outside of an IIS. I want to implement the caching by myself, so memcached isn't an option.
Right now I'm thinking of hosting a separate stateful System.ServiceModel.ServiceHost that does all the caching. It'll communicate with the rest of the WCF methods through a separate port or by some other means. However I'm not sure if this is the ideal solution to my problem. Has anyone got any tips?
I understand your confusion between stateless service and a stateful host and how the two can interact.
In this code sample I demonstrate conceptually how an in-memory singleton (Caching mechanism, I refer to as CachingProvider henceforth) can be referenced by both the service class (the service instance more precisely during the lifecycle of the request) and the service host (in this case I chose it to be a Console Application)
I assume here, the service interface and class are both located within the console applicaiton project that hosts the service.
In this simple example, my primitive CachingProvider class basically acts as a counter of how many service calls are made to the GetData method, and the service host will poll the CachingProvider every 5 seconds to get the count of service calls made so far.
note: you can use the WCFTestClient utility to test this quickly.
Disclaimer: I by no means suggest that a complex Caching mechanism be implemented as simply as in this sample, this code is merely for demosntration purposes.
namespace ServiceHostConsole
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
string GetData(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class TestService : ITestService
{
public TestService()
{
CachingProvider.CallCount++;
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
//For demonstration purposes only
static class CachingProvider
{
static CachingProvider()
{
//private static constructor can initialize
//static cacheable resources
_callCounter = 0; //Trivial example of initialization
}
private static int _callCounter;
public static int CallCount
{
set { _callCounter = value; }
get { return _callCounter; }
}
}
class Program
{
static void Main()
{
using (var host = new ServiceHost(typeof(TestService), new Uri("http://localhost/TestService")))
{
host.Open();
//Example how the ServiceHost can report on a persistent in-memory object that is being
//updated each time the service is called.
new Timer(state => Console.WriteLine("# of service calls: {0}", CachingProvider.CallCount), null, 0, 5000);
Console.Read();
host.Close();
}
}
}
}
I am very new to WCF and I am trying to learn but I think I am missing something significant here and I am aware of that so please be kind. I am working with a pre-existing console application that I have added a WCF host to, this is an oversimplified version of it but it should give you the jist of it
namespace mynamespace
{
public class MyConsoleApp
{
static void Main(string[] args)
{
CreateRemoteDebugHost();
StartLongRunningMethods();
}
public static void StartLongRunningMethods()
{
LongRunningMethod1();
LongRunningMethod2();
}
public static void LongRunningMethod1()
{}
public static void LongRunningMethod2()
{}
public void CreateRemoteDebugHost()
{
ServiceHost host = new ServiceHost(typeof(RemoteDebug), new Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(IRemoteDebug), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipeRemoteDebug");
//Create mex
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:8001/RemoteDebug");
host.Description.Behaviors.Add(smb);
host.Open();
}
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IRemoteDebugCallback))]
public interface IRemoteDebug
{
[OperationContract]
string Message(string message);
}
public interface IRemoteDebugCallback
{
[OperationContract(IsOneWay = true)]
void OnMessage(string callbackValue);
}
public class RemoteDebug : IRemoteDebug
{
public string Message(string message)
{
IRemoteDebugCallback callback = OperationContext.Current.GetCallbackChannel<IRemoteDebugCallback>();
callback.OnMessage(message);
return message;
}
}
}
As you can probably tell I am trying to send debug or status messages back to a client(s) from inside of long running static methods. All the plumbing seems to be working correctly, the host comes up, I can add a service reference to my client application just fine but the trouble starts when try to invoke the WCF callback from the longrunningprocesses static methods. I can't seem to figure out how to do that properly.
What is also very confusing is that almost every example I have seen of WCF and callbacks assumes that everything you are doing is running from within the context of the WCF host itself, obviously in my example this is not the case. I know I'm probably going aobut this all wrong so could someone please set me straight on this? Any help is greatly appreciated.
TIA!
There is client (not to be confused with the client program) created as well through app.config or manually (e.g. public class MyClient: ClientBase<IRemoteDebug> or public class MyClient: DuplexClientBase<IRemoteDebug>, IRemoteDebug). This should send messages to the client programs. Example using DuplexClient above from some code I had:
[CallbackBehaviorAttribute(UseSynchronizationContext = true)]
public class SubCallback : IRemoteDebug
{
public void Event(SomeClass evt)
{
// some handling code using:
//public delegate void EventCallbackHandler(SomeClass evt);
}
}
InstanceContext ctx = new InstanceContext(new SubCallback ());
MyClient _client = new MyClient(
ctx,
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
new EndpointAddress("net.pipe://localhost/ServiceEndpointName"));
Also, you may want to pass some options to your service, such as:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class RemoteDebug : IRemoteDebug
{}
It could be many things causing your particular issue, but this solved problems for me.
I am trying to migrate my .net remoting code to wcf but I'm finding it difficult. Can someone help me migrate this simple Remoting based program below to use WCF? The program implements a simple publisher/subscriber pattern where we have a single TemperatureProviderProgram that publishers to many TemperatureSubcriberPrograms that subcribe to the TemperatureProvider.
To run the programs:
Copy the TemperatureProviderProgram and TemperatureSubcriberProgram into seperate console application projects.
Copying to remaining classes and interfaces into a common Class Library project then add a reference to System.Runtime.Remoting library
Add a reference to the Class Library project from the console app projects.
Complie and run 1 TemperatureProviderProgram and multiple TemperatureSubcriberProgram.
Please note no IIS or xml should be used. Thanks in advance.
public interface ITemperatureProvider
{
void Subcribe(ObjRef temperatureSubcriber);
}
[Serializable]
public sealed class TemperatureProvider : MarshalByRefObject, ITemperatureProvider
{
private readonly List<ITemperatureSubcriber> _temperatureSubcribers = new List<ITemperatureSubcriber>();
private readonly Random randomTemperature = new Random();
public void Subcribe(ObjRef temperatureSubcriber)
{
ITemperatureSubcriber tempSubcriber = (ITemperatureSubcriber)RemotingServices.Unmarshal(temperatureSubcriber);
lock (_temperatureSubcribers)
{
_temperatureSubcribers.Add(tempSubcriber);
}
}
public void Start()
{
Console.WriteLine("TemperatureProvider started...");
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
TcpServerChannel tcpChannel = new TcpServerChannel("TemperatureProviderChannel", 5001, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
RemotingServices.Marshal(this, "TemperatureProvider", typeof(ITemperatureProvider));
while (true)
{
double nextTemp = randomTemperature.NextDouble();
lock (_temperatureSubcribers)
{
foreach (var item in _temperatureSubcribers)
{
try
{
item.OnTemperature(nextTemp);
}
catch (SocketException)
{}
catch(RemotingException)
{}
}
}
Thread.Sleep(200);
}
}
}
public interface ITemperatureSubcriber
{
void OnTemperature(double temperature);
}
[Serializable]
public sealed class TemperatureSubcriber : MarshalByRefObject, ITemperatureSubcriber
{
private ObjRef _clientRef;
private readonly Random portGen = new Random();
public void OnTemperature(double temperature)
{
Console.WriteLine(temperature);
}
public override object InitializeLifetimeService()
{
return null;
}
public void Start()
{
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
int port = portGen.Next(1, 65535);
TcpServerChannel tcpChannel = new TcpServerChannel(string.Format("TemperatureSubcriber_{0}", Guid.NewGuid()), port, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
ITemperatureProvider p1 = (ITemperatureProvider)RemotingServices.Connect(typeof(ITemperatureProvider), "tcp://localhost:5001/TemperatureProvider");
_clientRef = RemotingServices.Marshal(this, string.Format("TemperatureSubcriber_{0}_{1}.rem", Environment.MachineName, Guid.NewGuid()));
p1.Subcribe(_clientRef);
}
}
public class TemperatureProviderProgram
{
static void Main(string[] args)
{
TemperatureProvider tp = new TemperatureProvider();
tp.Start();
}
}
public class TemperatureSubcriberProgram
{
static void Main(string[] args)
{
Console.WriteLine("Press any key to start TemperatureSubcriber.");
Console.ReadLine();
TemperatureSubcriber ts = new TemperatureSubcriber();
ts.Start();
Console.ReadLine();
}
}
In WCF, with a "push" from the server you're really talking about duplex comms; the MarshalByRefObject is largely redundant here (AFAIK). The page here discusses various scenarios, including duplex/callbacks.
If the issue is xml (for some philosophical reason), then simply using NetDataContractSerializer rather than DataContractSerializer might help.
The other approach is to have the clients "pull" data periodically; this works well if you need to support basic http, etc.
What it sounds like you want to do is use WCF NetTcpBinding with Callbacks.
Take a look at this: http://www.codeproject.com/KB/WCF/publisher_subscriber.aspx
"Learning WCF" by Michele Bustamante is also very good. You can get Chpt1 for VS2008 at her website along with the code for the book. Chpt1 will explain/demo setting up connections and such. She also has downloadable sample code. One of the Samples is a DuplexPublishSubscribe.
You will need to modify your logic a bit. If you want to migrate this app to WCF. You will need to have clients pull data from the service at regular intervals.
You will also need a Windows service or application to host the WCF like the console you are using in the previous code.
Well I build real time systems so polling is not an option - I need to push data.
Also I am finding there is no WCF equivalent of System.Runtime.Remoting.ObjRef! This is an extremely useful type that encapsulates a service endpoint and can be serialise and passed around the network to other remoting service.
Think I’ll be sticking with good old remoting until the ObjRef equivalent is introduced.
Yes it is true, just one correction..
ObjRefs are created automatically when any MarshalByRefObject derived object is going outside the appdomain.
So in this case your ITemperatureProvider interface Subscribe method shoud take ITemperatureSubscriber instead of objref.
And then on client side just call p1.Subscribe(this) and the remoting layer will generate ObjRef from the object that will be serialized and sent. (sending b reference)