Error when setting WCF NetTcpBinding to only accept local connections - c#

I am trying to set up a WCF service which only accepts incoming messages/connection from itself.
I have been able to successfully create the service and run it and communicate with it using this code to create the WCF Endpoint (not restricted to localhost only)
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
_host = new ServiceHost(this, new Uri("net.tcp://localhost:19852"));
_host.Description.Behaviors.Add(new ServiceMetadataBehavior());
_host.AddServiceEndpoint(typeof(ISyncClient), binding, "SyncService");
_host.AddServiceEndpoint(typeof(IMetadataExchange), System.ServiceModel.Description.MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
_host.Open();
As soon as I add this line to restrict to connections from localhost
binding.HostNameComparisonMode = HostNameComparisonMode.Exact;
I get this exception
System.ServiceModel.AddressAlreadyInUseException: There is already a listener on IP endpoint 0.0.0.0:19852. This could happen if there is another application already listening on this endpoint or if you have multiple service endpoints in your service host with the same IP endpoint but with incompatible binding configurations. ---> System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
I'm not even sure what I am doing is the correct way to restrict WCF access, but obviously its not working. To me this looks like some sort of conflict with the MEX endpoint. As far as I know I NEED the mex endpoint so I can't get rid of it. Anyone point me in the direction of a solution?

The easy way to do this is with a named pipe binding. It only supports local calls. From Choosing a Transport:
When communication is required between different WCF applications on a
single computer, and you want to prevent any communication from
another machine, then use the named pipes transport.
Also, Mex points are completely optional. You can get rid of its endpoint and behavior without a problem.

Related

Set ServiceBehavior on channelfactory WCF Net.Pipe

I am hosting a Net.Pipe WCF service from a forms application, which runs on a server for mostly internal calculations. To improve on this I was tasked with creating a Rest shell around this service so it becomes reachable from outside of the server. I managed to connect to this service with ease, but as soon as I drop it on the live server My rest shell can no longer connect, I tried debugging this, but the main error message that gets logged is:
The server was unable to process the request due to an internal
error. For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute
or from the serviceDebug configuration behavior) on the server in
order to send the exception information back to the client, or turn on
tracing as per the Microsoft .NET Framework SDK documentation and
inspect the server trace logs.
Thing is that I connect to this service from code and I cannot figure out how to either convert the way I connect to a service host so I can add the Service behavior or add the behavior to my channel factory.
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
ServiceDebugBehavior behavior = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true };
EndpointAddress ep = new EndpointAddress("net.pipe://localhost/IPCService");
ChannelFactoryfactory = new ChannelFactory<RadanWrapper.IRadanContract>(binding);
// This line doesn't work
factory.Endpoint.EndpointBehaviors.Add(behavior as IServiceBehavior);
_channel = factory.CreateChannel(ep);
So the question is either: How do I connect the behavior to the channel factory, or alternatively, how can I connect to this net.pipe service through service host. (I am still looking into the second options)
I found the problem, I tried adding the behavior to the rest shell (connecting end), while it should have been added to the forms application (Hosting end) that was hosting the net.Pipe WCF
ServiceHost serviceHost = new ServiceHost(typeof(IPCService));
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
// Create new behavior, remove any existing behaviors and add this new one.
var behavior = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true };
serviceHost.Description.Behaviors.Remove(typeof(ServiceDebugBehavior));
serviceHost.Description.Behaviors.Add(behavior);
serviceHost.AddServiceEndpoint(typeof(IPCService), binding, "net.pipe://localhost/IPCService");
serviceHost.Open();
Good thing I now got an actually working error message, turns out I was missing a specific dll that didn't get build correctly during deployment to the server.

Handling ever changing baseAddress in WCF host

As stated in the question, I need to handle a changing baseAddress property of a WCF service host. I have a simple object that contains a picture's binary value. This will be sent from a remote computer (actually a multitude of different ones) to the server through a Web app on the server that requests a specific file name based on given files in a database.
To do this I have made the service and implemented the necessary contracts, but I am stuck at the baseAddress element in the service's config file.
The dilemma is that the remote machine's IP is dynamic. It changes at about 5 min intervals (externally provided) and the machine is not part of a VPN or any (inter)network and has no DNS configured. It only has its pc name set. I am however able to pull the IP of this device from a table to provide to the client app with the 'correct' IP to bind to, but my issue is rather at the host side.
The question is basically - in this described host, how do I specify the baseAddress that clients will bind to? I imagine I cannot rebind every time an IP address change is detected, or would that be no issue?
I have tried specifying "http://localhost:6090/BasicService/" which was unreachable when I attempted from the client to call "http://192.168.0.4:6090/BasicService/". However when I specify the exact IP in both client and host, the client successfully contacts the host.
Note that the binding of my service is basicHttpBinding and the private IP addresses are just in a test environment. When live, these devices receive their IP from m2m providers.
EDIT:
For better understanding, the remote device will be the server and contains the binding information which is XYZ and XYZ is what I am trying to figure out. The client can connect to a local machine to retrieve a specific remote device's IP and then bind the client to that given IP, but the issue is that in the host I am unsure as to what to use in the following:
host
baseAddresses
add baseAddress="http://????????:6090/"
baseAddresses
host
The suggested answer below does help when these devices are in a closed network and you call the server's private IP, but does not work with the server's public IP.
There is some confusion about your question (see comments). In this answer I assumed your clients do know the IP address of your server, but your problem is binding the server to an unkown IP address.
Server side
In order to listen to any ip address on your server side, you could use ip address 0.0.0.0. This way, on the server side, you don't need to worry about the ip address. Like so:
<host>
<baseAddresses>
<add baseAddress="http://0.0.0.0:6090/BasicService" />
</baseAddresses>
</host>
Client side
And on the client side, you can make the binding dynamic, by specifying it in code:
string ipAddress = GetIpFromTable(); // Assuming client can do this...
string url = $"http://{ipAddress}:6090/BasicService";
var binding = new BasicHttpBinding();
var address = new EndpointAddress(url);
var client = new BasicServiceClient(binding, address);
You may run into some trouble when the ip address changes during/before a request though, so make sure you have your error handling in order and retry if the first attempt failed.

How to make client automatically detect if WCF service is down or lost connection

I've looked at a bunch of threads like Detect if wcf service is activated but these solutions require the client to proactively detect if the WCF service is running. But what if I am in the middle of a transaction and the WCF service goes down or the connection is lost for some reason? In my testing there is no exception thrown; either nothing happens at all or that twirly circle thing just keeps going round and round. I want the client to detect if the service/connection is lost and gracefully tell the user it's down. I have timeouts set in my code:
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
binding.OpenTimeout = TimeSpan.FromSeconds(15);
binding.SendTimeout = TimeSpan.FromSeconds(3000);
binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
this._engineChannel = new DuplexChannelFactory<IEngineApi>(this, binding, new EndpointAddress("net.pipe://localhost/Engine"));
But if I am in the middle of a transaction nothing actually happens; these timeouts don't seem to affect anything.
You can use one of the two approaches:
1
The two things I do are a telnet check to make sure the WCF process
has the socket open.
telnet host 8080 The second thing I do is always add an IsAlive method
to my WCF contract so that there is a simple method to call to check
that the service host is operating correctly.
public bool IsAlive() {
return true; }
Source: Pinging WCF Services
2
Use the Discovery/Announcement feature introduced in WCF 4.0
Discovery depends on the User Datagram Protocol (UDP). UDP is a connectionless protocol, and there is no direct connection required between the client and server. The client usages UDP to broadcast finding requests for any endpoint supporting a specified contract type. The discovery endpoints that support this contract will receive the request. The implementation of the discovery endpoint responds back to the client with the address of the service endpoints. Once the client determines the services, it invokes the service to set up call.
Simple usage example: http://www.codeproject.com/Articles/469549/WCF-Discovery

Using an IP address for a WCF Service

I have a C# app that calls my WCF on my server.
At some time I will want to move part of my uploading process from client to server to a different server.
So, this is what I was going to do.
My desktop application invokes a WCF hosted on my server using the full DNS name ~ www.mysite.com.
That [web method] will then return an IP address to the client.
The client will then upload images to a DIFFERENT server that is accessible only by an IP address.
The idea is that if my app is successful and I would need to 'load-balance' the image uploads I would then have a mechanism to specify different destination servers to my clients.
So, I started looking in StackOverFlow for a way to dynamically set the IP address/End-Point of my WCF. This was easy to do for web services but it seems to me a bit more difficult for WCF.
Needless to say I was about to go through the answers here but came across a comment saying that the WCF needs to be using DNS.
Is this true?
As this is the only server I got and it is using DNS (I have not got access to a secondary server yet) I cannot test this out.
Are both things possible?
When you write a web service client, you can change the server endpoint programmatically. It has nothing to do with using WCF or not in the server.
MyClient client = new MyService.MyClient();
client.Endpoint.Address = new EndpointAddress(new Uri("target URL"));
client.Open();
You could find more details on the EndPointAddress MSDN reference.
Hope I helped!
MyClient client = new MyService.MyClient();
client.Endpoint.Address = new EndpointAddress(new Uri("target URL"));
client.Open();
It works, we could also use
myService.MyClient client = new myService.MyClient ();
//enter code here, when the service is created as wcf application

ServiceHost AddServiceEndpoint - multiple connections

I have a Duplex WFC client based on this which, when used with a single client works fine. The problem arises when I try to connect more than one client to the server. When I do, I get an exception: There was no endpoint listening at net.tcp://localhost:9080/MyDataService/1617f081e4b04c288965eea6ae18a39f that could accept the message. This is often caused by an incorrect address or SOAP action.
I define my endpoint like this:
ServiceHost duplex = new ServiceHost(typeof(ServerWCallbackImpl));
and add my client endpoint like this:
duplex.AddServiceEndpoint(typeof(IServerWithCallback), new NetTcpBinding(), uniqueEndpointAddress);
There is no problem with the first connection but when I attempt to connect with a second client in the same way, the above exception is thrown.
Does anyone see what I'm doing wrong?
Thanks in Adv!

Categories

Resources