What's the most simple way that a WCF Service, that's being hosted by a console application , can interact with the said console application, to do a Console.WriteLine() ie.
Some code:
The contract:
[ServiceContract(Name = "IProdsService")]
public interface IProdsService
{
[OperationContract(Name = "Alert",IsOneWay=true)]
void Alert(string msg);
}
The Service:
public class ProdsService : IProdsService
{
//IProdsService.Alert implementation
public void Alert(string msg)
{
//TODO: Send Alert to Console Application!
}
}
The Console App:
class Program
{
static void Main(string[] args)
{
ServiceHost prodService = new ServiceHost(typeof(ProdsService));
ServiceDescription serviceDesciption = prodService.Description;
prodService.Open();
Console.ReadLine();
}
}
Following is an example of running host and client where host can log message on the console. On your example, I am not sure why you have set IsOneWay=true. For this specific case, one way is not what you want. Also, I have used net.tcp binding in the following example; it should work with any other binding as well.
Basically in the example, user's entry is sent to the host service which echos the message on the console.
[ServiceContract]
public interface IProdsService
{
[OperationContract]
void Alert(string msg);
}
/// <summary>
/// Host Class
/// </summary>
public class ProdsService : IProdsService
{
public ProdsService()
{
Console.WriteLine("Service instantiated.");
}
public void Alert(string msg)
{
Console.WriteLine(msg);
}
}
/// <summary>
/// Client proxy wrapper
/// </summary>
public class ProdsServiceClient : ClientBase<IProdsService>, IProdsService
{
public ProdsServiceClient()
{
}
public ProdsServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public void Alert(string msg)
{
base.Channel.Alert(msg);
}
}
class Program
{
static ManualResetEvent _reset;
static void Main(string[] args)
{
string host = "localhost";
int port = 8888;
//ManualResetEvent is used for syncing start/stop of service.
_reset = new ManualResetEvent(false);
var action = new Action<string, int>(Start);
var result = action.BeginInvoke(host, port, null, null);
//Wait for svc startup, this can be synced with resetEvents.
Thread.Sleep(2000);
//Create a client instance and send your messages to host
using (var client = new ProdsServiceClient(new NetTcpBinding(), new EndpointAddress(string.Format("net.tcp://{0}:{1}", host, port))))
{
client.Alert("Test message");
string msg = string.Empty;
do
{
Console.Write("Type a message to send (X to exit): ");
msg = Console.ReadLine();
client.Alert(msg);
}
while (!msg.Trim().ToUpper().Equals("X"));
}
//Signal host to stop
_reset.Set();
action.EndInvoke(result);
Console.Write("Press any to exit.");
Console.ReadKey();
}
static void Start(string host, int port)
{
string uri = string.Format("net.tcp://{0}:{1}", host, port);
//var server = new ProdsService();
ServiceHost prodService = new ServiceHost(typeof(ProdsService));
prodService.AddServiceEndpoint(typeof(IProdsService), new NetTcpBinding(), uri);
Console.WriteLine("Service host opened");
prodService.Open();
//Wait until signaled to stop
_reset.WaitOne();
Console.WriteLine("Stopping host, please wait...");
prodService.Close();
Console.WriteLine("Service host closed");
}
}
duh, my bad, just calling console.writeline from the alert operation does it, i was being fooled by the oneway and was not getting an error...so that's no more oneways for me...
Related
is it even possible to send a message to all(or selected) connected clients connected to the hub? I mean from the server side to all clients. I can get clients data to server window, but when I enter and try to send from server side nothing happens on clients window. How to push notifications to all straight from server app?
Server:
namespace SignalRHub
{
class Program
{
static void Main(string[] args)
{
string url = #"http://localhost:8080/";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine(string.Format("Server running at {0}", url));
Console.ReadLine();
while (true)
{
// get text to send
Console.WriteLine("Enter your message:");
string line = Console.ReadLine();
// Get hub context
IHubContext ctx = GlobalHost.ConnectionManager.GetHubContext<TestHub>();
// call addMessage on all clients of context
ctx.Clients.All.addMessage(line);
// pause to allow clients to receive
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
[HubName("TestHub")]
public class TestHub : Hub
{
public void SendMsg(string message)
{
Console.WriteLine(message);
}
}
}
}
Client:
namespace SignalRClient
{
class Program
{
static void Main(string[] args)
{
IHubProxy _hub;
string url = #"http://localhost:8080/";
try
{
Console.WriteLine("Connecting to: " +url);
var connection = new HubConnection(url);
_hub = connection.CreateHubProxy("TestHub");
connection.Start().Wait();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("---------- Connection OK.");
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine("------------ Connection FAILED: ");
Console.WriteLine(e);
throw;
}
string line = null;
while ((line = System.Console.ReadLine()) != null)
{
_hub.Invoke("SendMsg", line).Wait();
}
Console.Read();
}
}
}
I have two processes and the second process needs to have access to a singleton in the first process. So I wrote a server that should help sharing the instance.
Something is wrong though, it seems like the client gets its own version of the singleton rather than the original instance.
The minimal example comes in two projects. Here is the client:
Program
using IPCServer;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace IPCEventTest
{
class IPCClient
{
static void Main(string[] args)
{
Process ipcserver = Process.Start("IPCServer.exe");
Thread.Sleep(2000);
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Connect");
Module module = Connect("WellKnownName"); //this name is used by the host
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Connect");
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Raise");
module.RaiseEvent(); //raise event should raise within the server process
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Raise");
while (true) ;
}
public static Module Connect(string id)
{
Console.WriteLine("Start Connect");
ChannelFactory<IModuleServer> pipeFactory =
new ChannelFactory<IModuleServer>(
new NetNamedPipeBinding(),
new EndpointAddress($"net.pipe://localhost/{id}")
);
IModuleServer serverProxy = pipeFactory.CreateChannel();
Module ret = serverProxy.GetModule();
Console.WriteLine("End Connect");
return ret;
}
}
}
The following files set up the Host:
Program
using System;
namespace IPCServer
{
class Program
{
static HOST host;
static void Main(string[] args)
{
host = new HOST("WellKnownName");
Module.Instance.myevent += Instance_myevent;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Subscribed to {Module.Instance.id}");
while (true) ;
}
private static void Instance_myevent(object sender, EventArgs e)
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Event Fired from {(sender as Module).id}");
}
}
}
Module
using System;
using System.Linq;
namespace IPCServer
{
public class Module
{
public static Module Instance { get; } = new Module();
public event EventHandler myevent = delegate { };
public string id;
private Module()
{
var guid4 = Guid.NewGuid().ToString().Take(4);
id = new String(guid4.ToArray());
Console.WriteLine($"Module Constructor {id}");
myevent += Module_myevent;
}
private void Module_myevent(object sender, EventArgs e)
{
Console.WriteLine($"Module Listener {(sender as Module).id}");
}
public void RaiseEvent()
{
Console.WriteLine($"Module Start Raise {id}");
myevent(this, EventArgs.Empty);
Console.WriteLine($"Module End Raise {id}");
}
}
}
Host
using System;
using System.ServiceModel;
namespace IPCServer
{
internal class HOST
{
ServiceHost host;
internal HOST(string id)
{
host = new ServiceHost(typeof(ModuleServer), new Uri[] { new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(IModuleServer), new NetNamedPipeBinding(), id);
host.Open();
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Opened");
}
~HOST()
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
host = null;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Destructed");
}
}
[ServiceContract]
public interface IModuleServer
{
[OperationContract]
Module GetModule();
}
public class ModuleServer : IModuleServer
{
public Module GetModule()
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer start GetModule");
Module ret = Module.Instance;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer end GetModule");
return ret;
}
}
}
The example runs and here is the output on my system:
Why am i not getting my Singleton from the server process.
Why is my event not raised in the server.
Edit: The Server opens the host and also subscribes to the singleton. After the client connects it raises the event via member function. Event has two subscribers, one in the constructor and one on the server side. only the Module internal subscription is handled - there is no eventhandling on the server side - no event is fired on the server side. The module listener is triggered but not inside the host process. This event is handled on the client side.
Why am i not getting my Singleton from the server process
Named pipes use Serialization to pass the object from server to client. That means that the client side have to re-run the constructor and copy existing properties.
public string id; is a field so it won't be 'copied' so the random value set by the constructor is left. That's why you have different Ids for the "same" object.
To solve this, you can change this to :
[DataContract]
public class Module
{
[DataMember]
public string id {get; set;}
}
Why is my event not raised in the server.
This is not how WCF named pipes works as you just have a duplicate version a client side. I suggest you to read about Duplex Channel
I have been trying to get my head around delegates in C#, but I just don't seem to get the point how to use them.
I have a program that should do the following with delegates:
Main.cs:
Create 1 server;
Create 3 clients;
Register clients to server;
Send message from server to all registered clients;
Unregister 1 client and send message from server to registered clients;
Send message from server to specific client.
Server.cs:
public delegate MsgSend(string message);
private MsgSend msgSend;
public void Register(MsgSend m);
public void Unregister(MsgSend m);
public void SendMessage(string message);
Clien.cs:
private string id;
public Client(string id); - explicit constructor;
public void ClientRegister(Server server);
public void ClientUnregister(Server server);
public void PrintMessage(string message);
How should I use the delegate int this situation...?
Any help with some explanations how and where things are happening in this program will be in great use for me.
Thanks in advance.
Here is the code:
Server:
public delegate string MsgSend(string message);
class Server
{
private MsgSend msgSend;
public void Register(MsgSend m)
{ // Is this the right way to use the delegate as multicast delegate
// to register the client and say it is registered or
// I should do extremely different thing here?
msgSend += new MsgSend(m);
m("xx");
}
public void Unregister(MsgSend m)
{ // Is this the right way to use the delegate as multicast delegate
// to register the client and say it is registered or
// I should do extremely different thing here?
msgSend += new MsgSend(m);
m("yy");
}
public void SendMessage(string message)
{
Console.WriteLine(message + this.msgSend);
}
}
Client:
class Client
{
public string id;
public Client(string id)
{
this.id = id;
}
public void ClientRegister(Server server)
{
server.Register(...); // What should I pass as parameter here and why...
}
public void ClientUnregister(Server server)
{
server.Unregister(...); // What should I pass as parameter here and why...
}
public void PrintMessage(string message)
{
Console.WriteLine(id + " recieved: " + message);
}
}
Main:
class Program
{
static void Main(string[] args)
{ // Is this enough in the main program so the program to work?
Server svr = new Server();
Client cl1 = new Client("123");
Client cl2 = new Client("456");
Client cl3 = new Client("789");
cl1.ClientRegister(svr);
cl2.ClientRegister(svr);
cl3.ClientRegister(svr);
svr.SendMessage("message from server");
}
}
Here is full working code:
Main
using System;
static partial class Program
{
static void Main()
{
Server svr = new Server();
Client cl1 = new Client("123");
Client cl2 = new Client("456");
Client cl3 = new Client("789");
// Register
cl1.ClientRegister(svr);
cl2.ClientRegister(svr);
cl3.ClientRegister(svr);
svr.SendMessage("message from server");
// Unregister
cl1.ClientUnregister(svr);
cl2.ClientUnregister(svr);
cl3.ClientUnregister(svr);
// Try send string when you unregistered all clients
svr.SendMessage("message from server again");
Console.ReadLine();
}
}
Server
class Server
{
public delegate void MsgSend(string message);
private MsgSend msgSend;
public void Register(MsgSend m)
{
msgSend += m;
m("Registered");
}
public void Unregister(MsgSend m)
{
msgSend -= m; // <--- Change += to -=
m("Unregistered");
}
public void SendMessage(string message)
{
//You have to check if msgSend is null:
if (msgSend != null) this.msgSend(message);
else Console.WriteLine("DONT HAVE ANY CLIENT!");
}
}
Client
class Client
{
public string id;
public Client(string id)
{
this.id = id;
}
public void ClientRegister(Server server)
{
server.Register(PrintMessage);
}
public void ClientUnregister(Server server)
{
server.Unregister(PrintMessage);
}
public void PrintMessage(string message)
{
Console.WriteLine("Client " + id + " recieved: " + message);
}
}
When run Main() you will receive:
Client 123 recieved: Registered
Client 456 recieved: Registered
Client 789 recieved: Registered
Client 123 recieved: message from server
Client 456 recieved: message from server
Client 789 recieved: message from server
Client 123 recieved: Unregistered
Client 456 recieved: Unregistered
Client 789 recieved: Unregistered
DONT HAVE ANY CLIENT!
I'm working with a windows service where i want to add a GUI too it. I made a proof of concept with my service creating a ServiceHost object and hosting the WCF Named pipes service and then i Console application to consume the WCF service and also getting callback responses from the service (messages sent from server to connected clients). This works great my console application runs and get responses from the service with no interruption or delays.
However when doing the same thing in my WPF GUI application when clicking a button that then calls the WCF service it freezes the whole UI thread and then after a couple of minutes throws an exception and then the UI is updated with the message callback (server sends message to connected clients) but any return values from service is lost since the exception was thrown.
The two exception messages i have gothen is theses (the most common is the first):
1: The requesting action sent to net.pipe :/ / localhost / PipeGUI did not receive a response within the specified timeout (00:00:59.9989999). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Raise the deadline for action (by entering the channel / proxy to Icon Text Channel and set the property Operation Timeout) and verify that the service can connect to the client.
2: Communication object System.ServiceModel.Channels.ServiceChannel, can not be used for communication because it has been canceled.
Anyone got any ideas why this is happeing ?
I can post more code if neccessary.
UPDATE , added code for reference
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void MessageRecived(string message);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
public interface IPipeServiceContract
{
[OperationContract]
string Hello();
[OperationContract]
void Message(string msg);
[OperationContract(IsInitiating = true)]
void Connect();
[OperationContract(IsTerminating = true)]
void Disconnect();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class PipeService : IPipeServiceContract
{
List<IClientCallback> _clients = new List<IClientCallback>();
public string Hello()
{
PublishMessage("Hello World.");
return "Return from method!";
}
public void Connect()
{
_clients.Add(OperationContext.Current.GetCallbackChannel<IClientCallback>());
}
public void Disconnect()
{
IClientCallback callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
_clients.Remove(callback);
}
void PublishMessage(string message)
{
for (int i = _clients.Count - 1; i > 0; i--)
{
try
{
_clients[i].MessageRecived(message);
}
catch (CommunicationObjectAbortedException coae)
{
_clients.RemoveAt(i);
}
catch(CommunicationObjectFaultedException cofe)
{
_clients.RemoveAt(i);
}
}
}
public void Message(string msg)
{
PublishMessage(msg);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged, IClientCallback
{
public ServiceController Service { get; set; }
protected IPipeServiceContract Proxy { get; set; }
protected DuplexChannelFactory<IPipeServiceContract> PipeFactory { get; set; }
public ObservableCollection<ServerActivityNotification> Activity { get; set; }
public override void BeginInit()
{
base.BeginInit();
PipeFactory = new DuplexChannelFactory<IPipeServiceContract>(this, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeGUI"));
}
public MainWindow()
{
InitializeComponent();
Activity = new ObservableCollection<ServerActivityNotification>();
Service = ServiceController.GetServices().First(x => x.ServiceName == "Server Service");
NotifyPropertyChanged("Service");
var timer = new DispatcherTimer();
timer.Tick += new EventHandler(OnUpdate);
timer.Interval = new TimeSpan(0, 0, 0, 0, 850);
timer.Start();
if (Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
void OnUpdate(object sender, EventArgs e)
{
Service.Refresh();
NotifyPropertyChanged("Service");
StartButton.IsEnabled = Service.Status != ServiceControllerStatus.Running ? true : false;
StopButton.IsEnabled = Service.Status != ServiceControllerStatus.Stopped ? true : false;
if (PipeFactory != null && Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private void OnStart(object sender, RoutedEventArgs e)
{
try
{
Service.Start();
}
catch
{
Service.Refresh();
}
}
private void OnStop(object sender, RoutedEventArgs e)
{
try
{
if (Proxy != null)
{
Proxy.Disconnect();
PipeFactory.Close();
}
Service.Stop();
}
catch
{
Service.Refresh();
}
}
public void MessageRecived(string message)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
ServerActivityNotification log = new ServerActivityNotification { Activity = message, Occured = DateTime.Now };
Activity.Add(log);
ListBoxLog.ScrollIntoView(log);
NotifyPropertyChanged("Activity");
}));
}
private void OnHello(object sender, RoutedEventArgs e)
{
try
{
Proxy.Message(txtSendMessage.Text);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
Try setting the UseSynchronizationContext property of the service behaviour to false:
[ServiceBehavior(UseSynchronizationContext = false)]
class MyService
{
}
[ServiceContract]
public interface IMyService
{
}
I believe that by default this is set to true, so you are currently attempting to consume and run the WCF service on the same thread resulting in a deadlock.
In any case, it sounds like you are trying to consume the WCF service on the UI thread of the WPF application. Generally it is recommended that you perform potentially long running tasks on the background thread as this keeps the interface responsive even if your service call takes a few seconds/minutes.
EDIT:
I tried and succeeded in replicating your problem. Trying to call the service on the UI thread results in the UI freezing. However, when I changed my code to call the service on a background task (see below), I was able to call the service and receive the callback:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
var myService = DuplexChannelFactory<IMyService>.CreateChannel(new CallbackImplementation(),
new WSDualHttpBinding(),
new EndpointAddress(
#"http://localhost:4653/myservice"));
myService.CallService();
string s = "";
});
}
I have to confess, I am not entirely sure why this is so, and any clarification on exactly how WCF manages the thread hosting the service instance would be great in working out why this works.
I have a Console application hosting a WCF service. I would like to be able to fire an event from a method in the WCF service and handle the event in the hosting process of the WCF service. Is this possible? How would I do this? Could I derive a custom class from ServiceHost?
You don't need to inherit from ServiceHost. There are other approaches to your problem.
You can pass an instance of the service class, instead of a type to ServiceHost. Thus, you can create the instance before you start the ServiceHost, and add your own event handlers to any events it exposes.
Here's some sample code:
MyService svc = new MyService();
svc.SomeEvent += new MyEventDelegate(this.OnSomeEvent);
ServiceHost host = new ServiceHost(svc);
host.Open();
There are some caveats when using this approach, as described in http://msdn.microsoft.com/en-us/library/ms585487.aspx
Or you could have a well-known singleton class, that your service instances know about and explicitly call its methods when events happen.
using ...
using ...
namespace MyWCFNamespace
{
class Program {
static void Main(string[] args){
//instantiate the event receiver
Consumer c = new Consumer();
// instantiate the event source
WCFService svc = new WCFService();
svc.WCFEvent += new SomeEventHandler(c.ProcessTheRaisedEvent);
using(ServiceHost host = new ServiceHost(svc))
{
host.Open();
Console.Readline();
}
}
}
public class Consumer()
{
public void ProcessTheRaisedEvent(object sender, MyEventArgs e)
{
Console.WriteLine(e.From.toString() + "\t" + e.To.ToString());
}
}
}
namespace MyWCFNamespace
{
public delegate void SomeEventHandler(object sender,MyEventArgs e)
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class WCFService : IWCFService
{
public event SomeEventHandler WCFEvent;
public void someMethod(Message message)
{
MyEventArgs e = new MyEventArgs(message);
OnWCFEvent(e);
}
public void OnWCFEvent(MyEventArgs e)
{
SomeEventHandler handler = WCFEvent;
if(handler!=null)
{
handler(this,e);
}
}
// to do
// Implement WCFInterface methods here
}
public class MyEventArgs:EventArgs
{
private Message _message;
public MyEventArgs(Message message)
{
this._message=message;
}
}
public class Message
{
string _from;
string _to;
public string From {get{return _from;} set {_from=value;}}
public string To {get{return _to;} set {_to=value;}}
public Message(){}
public Message(string from,string to)
this._from=from;
this._to=to;
}
}
You can define your WCF service with InstanceContextMode = InstanceContextMode.Single.
TestService svc = new TestService();
svc.SomeEvent += new MyEventHandler(receivingObject.OnSomeEvent);
ServiceHost host = new ServiceHost(svc);
host.Open();
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] // so that a single service instance is created
public class TestService : ITestService
{
public event MyEventHandler SomeEvent;
...
...
}