Question: Is there a way how to quickly check whether particular pipename is being hosted in session 0 - preferabely during the ServiceHost.Open call?
Scenario:
Two processes PipeHost and PipeUser are trying to communicate on the system via pipe with name PeacePipe. They are not required to be started with special privileges.
PipeHost is started and hosts the PeacePipe without any problem.
PipeUser is started and connects to PeacePipe without any problem.
PipeUser tries to comunicate to PipeHost via PeacePipe, it sends messages but PipeHost doesn't see anything.
In fact PipeUser connected to DifferentPipeHostInSession0 that is hosting pipe with same name (but OS creates different pipe) in an elevated (or service) process.
Background:
ServiceHost.Open should throw AddressAlreadyInUseException when the selected pipename is already being hosted.
However it's not thrown if the pipe is hosted in session 0 and you are attempting to host the same pipe in different sessions. As windows named pipes are normally not te be used accross sessions. With the exception of pipe hosted in session 0. Any process can connect to such a pipe. This can lead to the above sceanrio.
Code:
[ServiceContract]
public interface IService
{
[OperationContract]
void Ping();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults = true)]
public class Service: IService
{
public void Ping()
{
Console.WriteLine("Client Pinged me");
}
}
private static readonly Uri _pipeUri = new Uri("net.pipe://localhost/aaa");
private static readonly Binding _pipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
static void PipeHostTest()
{
ServiceHost serviceHost = new ServiceHost(new Service(), _pipeUri);
serviceHost.AddServiceEndpoint(typeof(IService), _pipeBinding, "");
try
{
//Fail here if same pipe already opened - even in Global space
serviceHost.Open();
Console.WriteLine("OPENED");
}
catch (AddressAlreadyInUseException e)
{
Console.WriteLine(e);
throw;
}
Console.ReadKey();
}
static void PipeClient()
{
ChannelFactory<IService> channelFactory =
new ChannelFactory<IService>(_pipeBinding, new EndpointAddress(_pipeUri));
var proxy = channelFactory.CreateChannel();
proxy.Ping();
}
static void Main(string[] args)
{
if (args.Any())
{
PipeClient();
}
else
{
PipeHostTest();
}
}
Run once without parameters elevated, once without parameters non-elevated. Both processes will host pipe with same name - but those are different pipes.
Then run once with any parameter. Client process will conect to the pipe hosted by elevated process.
Possible Solution:
Use a named mutex in global session new Mutex(true, "Global\\MutexForMyPipeName", out createdNew) to see if there is another process trying to do the same.
This however disqualifies even scenarios where the pipes are in 2 different sessions that do not colide.
Preferabely the ServiceHost.Open would take care about this for me as I'm using multiple bindings types (net.tcp, net.pipe, net.udp) and have single code for creating and hosting the ServiceHost. NamedPipes are the only ones that can allow creation of new host without AddressAlreadyInUseException exception while the address is actuall already in use.
Related
If I use this code for self Host a WCF service in a Console application it works. I run the host app and then from another app (which I call the client app,) I can add the service reference from visual studio > solution explorer > ADD SERVICE REFERENCE > http://10.131.131.14:8080/sendKioskMessage > click GO, add the service with no problems and consume it from the client app (which is a windows form)
But if I run the same code in a Windows Form, I run first the (SELF HOST WCF) windows form app, then from the other app (client app) in visual studio I try to add the service reference from ADD SERVICE REFERENCE in solution explorer (Just the same way that it works before but with the Console App self host) but it throws the following error:
*
An error (Details) occurred while attempting to find services at
http://10.131.131.14:8080/sendKioskMessage.
(If I click Details Link, says the following:)
There was an error downloading
'http://10.131.131.14:8080/sendKioskMessage/$metadata'. Unable to
connect to the remote server. Metadata contains a reference that
cannot be resolved: 'http://10.131.131.14:8080/sendKioskMessage'.
There was no endpoint listening at
http://10.131.131.14:8080/sendKioskMessage that could accept the
message. This is often caused by an incorrect address or SOAP action.
See InnerException, if present, for more details. Unable to connect to
the remote server. If the service is defined in the current solution,
try building the solution and adding the service reference again.
*
The IP that I use is the IP of my pc where both apps are running. I also used localhost instead of my actual IP with the same result.
Windows Form Code (can't add the service from another app):
public partial class KioskosServerForm : Form
{
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
public KioskosServerForm()
{
InitializeComponent();
}
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
}
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
}
Console App Code (Works! I can add the service from other client app):
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/sendKioskMessage");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService),baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
I don't know why I can consume the service if the service is self hosted in a console app, but I can't add it if the service is self hosted in a Windows Form.
I will appreciate a lot your help to achieve this from a Windows From, since I need to self host the WCF service from a windows form, no a console app.
I'm using Visual Studio 2017, .Net Framework 4.6.1
THANKS IN ADVANCE GUYS!!
TL;DR the console app works because you have a delay before shutting down the service; the WinForms host doesn't
The reason your console WCF host service works is that you start the hosting and continue until the Console.ReadLine() line:
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine(); // <-------- program waits here
// Close the ServiceHost.
host.Close();
...after which the service is torn down. Prior to that, your other clients can connect fine and add Service References.
The WinForms app has no such delay:
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open(); // <------ opened here
} // <------ shutdown here
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
...it is immediately shutdown when the code goes out of scope of the using block. The using will automatically call Dispose() on the host object which in turn calls Close().
Consider placing the host into a variable like so:
ServiceHost _host; // <---------- new!
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
_host = new ServiceHost(typeof(KioskMessageService), baseAddress))
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
_host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
_host.Open();
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
Later, you can close the _host instance with a call to Close.
I have SignalR server as Class Library Project and i referenced it in Console application (to simulate Windows service)
Here is code for SignalR
public void Start()
{
try
{
string url = #"http://*:8081";
using (WebApp.Start<Startup>(url))
{
Logger.Info(string.Format("Server running at {0}", url));
}
}
catch (Exception ex)
{
Logger.Exception(ex, "Signalr start");
}
Run = true;
Logger.Info("Starting Worker");
workerThread = new Thread(() =>
{
Worker();
});
workerThread.Start();
}
And here is Startup class
public class Startup
{
Microsoft.AspNet.SignalR.HubConfiguration hubconfiguration = null;
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
hubconfiguration = new HubConfiguration();
hubconfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubconfiguration);
}
}
So, it is in one thread, and worker is in another. That seems fine since i did it in other project where it works. Worker thread isn't problem, it's just empty loop, not related to server in any way.
Problem is that server seems to "stop" - when i look with Netstat, nobody is listening on port 8081. There is no exception, it just silently fails.
I referenced Owin.Cors (and Owin.Host.HttpListener) in console project that actually runs this server but as I said, server just stops.
When I try to connect, client says "connection actively refused" and Putty (telnet) also says "can't connect".
Where is the problem? In a nutshell, i have Class Library with SignalR server that is referenced in Console project that runs it but server just wont work.
[edit]
And there is code of Console app that starts service
static void Main(string[] args)
{
ServiceEngine Engine = new ServiceEngine();
Engine.Start();
Console.ReadKey();
Engine.Stop();
}
P.S. Sorry for my bad English.
Well, i solved it. Here was a problem:
public static void Start()
{
try
{
string url = #"http://127.0.0.1:8081";
WebApp.Start<Startup>(url);
Logger.Info(string.Format("Server running at {0}", url));
}
catch (Exception ex)
{
Logger.Exception(ex, "signalr start");
}
Run = true;
Logger.Info("Starting Worker");
workerThread = new Thread(() =>
{
Worker();
});
workerThread.Start();
}
As you can see, using statement was removed and now it works fine! Interesting note - you can also make Singleton implementation of this "Engine", and it will also work.
I got Solution after doing lot of R & D. And Its a Simple change related to Account and Access Rights.
Use LocalSystem Account instead of LocalService Account in Service Installer.
You can do this either from doing below change in design view of your service installer:
Properties of Service Process Installer -> Set Account to LocalSystem.
or by doing below change in in designer.cs file of your service installer:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
Using the code from this answer - Async two-way communication with Windows Named Pipes (.Net) - I'm finding the maximum number of connections/clients at any one time is 10.
In the crude example below (this uses multiple threads - same thing happens if multiple processes are used) clients 1 to 10 will start and run as normal. However clients 11 and 12 will block when 'ProcessData' is called, eventually throwing a TimeoutException.
public static void Start()
{
// Start Server
new Thread(new ThreadStart(Server.MainRun)).Start();
// Start Clients
for (int i = 1; i <= 12; i++)
{
Thread.Sleep(500);
Client c = new Client(i.ToString());
new Thread(new ThreadStart(c.Run)).Start();
}
}
// Create a contract that can be used as a callback
public interface IMyCallbackService
{
[OperationContract(IsOneWay = true)]
void NotifyClient();
}
// Define your service contract and specify the callback contract
[ServiceContract(CallbackContract = typeof(IMyCallbackService))]
public interface ISimpleService
{
[OperationContract]
string ProcessData();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class SimpleService : ISimpleService
{
public string ProcessData()
{
// Get a handle to the call back channel
var callback = OperationContext.Current.GetCallbackChannel<IMyCallbackService>();
callback.NotifyClient();
return DateTime.Now.ToString();
}
}
class Server
{
public static void MainRun()
{
// Create a service host with an named pipe endpoint
using (var host = new ServiceHost(typeof(SimpleService), new Uri("net.pipe://localhost")))
{
host.AddServiceEndpoint(typeof(ISimpleService), new NetNamedPipeBinding(), "SimpleService");
host.Open();
Console.WriteLine("Simple Service Running...");
Console.ReadLine();
host.Close();
}
}
}
class Client : IMyCallbackService
{
string _id;
public Client(string ID)
{
_id = ID;
}
public void Run()
{
Console.WriteLine("Starting client : " + _id);
// Consume the service
var factory = new DuplexChannelFactory<ISimpleService>(new InstanceContext(this), new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/SimpleService"));
var proxy = factory.CreateChannel();
Console.WriteLine(proxy.ProcessData());
Console.WriteLine("Client finished : " + _id);
}
public void NotifyClient()
{
Console.WriteLine("Notification from Server");
}
}
If the client closes the channel when done (factory.Close()) then all clients will be able to run.
I understand this question - Number of Clients that can connect to a Named Pipe - is very similar but suggests there is no low limit.
This suggests the limit is 10 on Windows XP and 2000 machines - http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.aspx - except this is happening on a Windows 8 machine and Windows 2008 server.
Is there a way to change this limit? Am I missing something obvious?
Google brought me here a year after this question was asked. I figure I may as well post to help anyone else who ends up here. I think I know why the limit of 10.
Are you aware of the NetNamedPipeBinding.MaxConnections property?
Gets or sets the maximum number of connections, both inbound and outbound, that are allowed to endpoints configured with the named pipe binding. ... The maximum number of named pipe connections that are allowed with this binding. The default value is 10.
"guest" is correct and an old blog post from MSDN corroborates this as still being applicable in current .net releases.
It also suggests that the default setting was defined for use with development environments and "small" deployments.
From the other settings (eg, the buffer size) I'd suggest that >8kb per connection overhead would be expected.
I have not yet found any information on what issues may arise if the value is tuned for larger values (eg, >1000): the API appears tuned for shorter, burstier requests and I suspect for large values it may simply be inefficient (not so much for memory but just internal implementation) -
I'd welcome evidence either way on performance/issues (or success) with significant numbers of clients attaching to an endpoint.
I am trying to make my first WCF program. It should be very simple but I can't manage to get it working.
Steps I have done so far:
Create C# windows forms application
Adding a button
Added a WCF Service to project.
Changed the codes according to this and this page tutorials.
IWCF.cs code :
using System;
...etc
namespace WCF_Application
{
[ServiceContract]
public interface IWCF
{
[OperationContract]
string DoWork();
}
}
WCF.cs code :
using System;
...
namespace WCF_Application
{
public class WCF : IWCF
{
public string DoWork()
{
return " Hello There! ";
}
}
}
Button code:
private void btnConnect_Click(object sender, EventArgs e)
{
// Starting the Server
ServiceHost svh = new ServiceHost(typeof(WCF));
svh.AddServiceEndpoint(
typeof(IWCF),
new NetTcpBinding(),
"net.tcp://localhost:8000");
svh.Open();
MessageBox.Show("Connected");
// Connecting Client to the server
ChannelFactory<IWCF> scf;
scf = new ChannelFactory<IWCF>(
new NetTcpBinding(),
"net.tcp://localhost:8000");
IWCF s = scf.CreateChannel();
string response = s.DoWork();
MessageBox.Show(response);
svh.Close();
}
I don't get the response message. The program justs freezes. When I trace the code, the program gets stuck at the strong response = s.DoWork(); line.
I also checked netstat -a in cmd. and it seems the port 8000 does open in listening mode. So the problem should be with the client part.
OS Windows 7 /
VS 2010 /
C#
You are running into a deadlock issue here because your WCF service is hosted on the same thread as your UI. You could try the following to host the service on another thread.
[ServiceBehavior(UseSynchronizationContext = false)]
public class WCF : IWCF
{
public string DoWork()
{
return " Hello There! ";
}
}
Here is some background on the WPF threading model and UseSynchronizationContext. Alternatively, you could thread off the client calls to your service, but that will likely have other UI updating consequences as well.
The main goal is to let users send message to the host. The host will think for two seconds, and then with DUAL, will send a message back. This is working fine for me.
The thing is, for every user who send a message, I'm subscribing him to a list. The thing I'm trying to accomplish is, if the user Console.Readline() == "b" ( brodadcast ), send all the subscribers "Hello".
But the list of subscribers is at the service, and the Console.Readline() is at the host.
The host:
static void Main(string[] args)
{
ServiceHost duplex = new ServiceHost(typeof (ServiceClass));
duplex.Open();
Console.WriteLine("Press 'b' To Broadcast All subscibers : Hello");
if (Console.ReadLine()=="b")
{
foreach (var registered in lstOfRegisteredUsers) //<== I cant access lstOfRegisteredUsers because its on the Service Class.
{
registered.SendBack("Hello");
}
}
Console.WriteLine("Host is running, press <ENTER> to exit.");
Console.ReadLine();
}
The service:
public class ServiceClass : ISend
{
public List<ISendBack> lstOfRegisteredUsers = new List<ISendBack>();
public void Send(string data)
{
ISendBack callback = OperationContext.Current.GetCallbackChannel<ISendBack>();
lstOfRegisteredUsers.Add(callback); // <== here i'm adding subscribers for future broadcast " hello".
Console.WriteLine("goind to process " + data);
System.Threading.Thread.Sleep(2000);
callback.SendBack("done " +data);
}
}
How can I send from the host, to each of the subscribers: "hello"?
If I understand correctly then you wish to broadcast a message to each of your service's callback clients if a key-press input is received into the console application hosting your service?
To do this you need to be able to call into your service instance from your host application.
This can be achieved by creating an instance of your service class within your host. Then when you create your ServiceHost you pass the instance into the ServiceHost constructor. Then in your service you have a method which does the actual callback and you can call it.
For example:
// Create an instance of your service class
ServiceClass sc = new ServiceClass();
// Instantiate new ServiceHost and pass in the instance as a singleton
serviceHost = new ServiceHost(sc, "(Service uri here)");
// Call the method on the service (which then calls the clients)
sc.DoCallbacks();