Handling ever changing baseAddress in WCF host - c#

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.

Related

HttpClient.GetAsync times out when connected to VPN

C# 4.5.2 framework HttpClient.GetAsync() method works fine on Windows 10 when system is not using VPN.
When VPN is connected HttpClient.GetAsync() call to the same address just blocks until it times out. Both Edge and Chrome have no issues accessing that same address.
Is there a way to see what is happening? What is HttpClient doing differently?
Update: Got some interesting clues by calling Dns.GetHostEntry(). Without VPN
this call returned only IPv4 addresses that all could be connected to. With VPN client connected Dns.GetHostEntry() returned additional IPv6 addresses at the top of the list. Connection to all IPv6 addresses timed out but all IPv4 ones still worked OK. Now is there a way to figure out without trying to connect which addresses work and which ones do not?
In my experience, this sounds like a VPN / firewall issue to me. One quick thing to toggle in windows is under you VPN adapter properties, try unchecking "Use default gateway on remote network" - I know it sounds like a long shot but have had this problem in the past...
Have to answer this myself as this problem has a simple cause but very confusing symptoms.
The root cause:
DNS reports only IPv4 addresses for the host when system is not connected to VPN. All IPv4 addresses are usable.
When VPN connection is active DNS returns IPv6 addresses in addition to IPv4. IPv4 addresses are still accessible but IPv6 are not.
The cause of such invalid network configuration is still a mystery that deserves its own separate post.
Confusing part:
Some apps work no matter what VPN connection status is.
"But web browser can connect to the same host with or without VPN." True. Browsers may use Happy eyeballs approach attempting to connect using both IPv4 and IPv6 at the same time.
"But my old app has not problems connecting." Also true. Some older and not so old apps use IPv4 protocol by default. Support for IPv6 or IPv4+IPv6 has to be explicitly implemented.
"But it works sometimes". This happens when VPN connections are not reliable. It leads to all sorts of solutions that are mere coincidences.
What exactly is happening:
HttpClient.GetAsync() uses default DNS resolution and can connect using both IPv4 and IPv6 addresses. It does not discriminate and there is no direct way to influence protocol selection. If DNS returns inaccessible address then HttpClient may use that invalid address to connect resulting in timeout.
Possible workarounds:
The best: ask IT to fix IPv6 DNS issues. DNS should not report inaccessible addresses.
Good: implement Happy eyeballs approach. Connect to both IPv6 and IPv4 host addresses using numeric IP instead of automatic resolution using host name.
OK: Always connect to IPv4 using numeric IP.
Here is the piece of code that shows how to connect to a specific IP address:
// Get DNS entries for the host.
var hostEntry = Dns.GetHostEntry(uri.Host);
// Get IPv4 address
var ip4 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetwork);
// Build URI with numeric IPv4
var uriBuilderIP4 = new UriBuilder(uri);
uriBuilderIP4.Host = ip4.ToString());
var uri4 = uriBuilder4.Uri;
// Get IPv6 address
var ip6 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetworkV6);
// Build URI with numeric IPv6
var uriBuilderIP6 = new UriBuilder(uri);
uriBuilderIP6.Host = $"[{ip6}]";
var uri6 = uriBuilder6.Uri;
For HTTPS connections numeric addresses work only with "host" header with the name of the host (not an IP address) in it. Here is the way to add it.
var client = new HttpClient();
// Add "host" header with real host name e.g. stackoverflow.com
client.DefaultRequestHeaders.Add("Host", uri.Host);

.NET Remoting Bind to All NICs

I am working on my companies .NET remoting server application to try and make it bind to all available NICs, so that it listens on multiple subnets.
The current way we do it is by telling the server to bind to a specific IP address and that has worked in the past, but when hosted on a server with multiple subnets the clients all need to be on the same subnet or they won't be able to speak to the server.
One solution I tried was to tell the server to bind to the machine name of the server it was running on using the following code:
// If there is an IP present in the config to bind to, then bind on that specific IP.
if (!string.IsNullOrEmpty(remotingBindTo))
{
props.Add("bindTo", remotingBindTo);
}
// Otherwise bind on the machine name, meaning the server should bind on all IP addresses.
else
{
props.Add("machineName", Environment.MachineName);
props.Add("useIpAddress", false);
}
// Create a new channel
IChannel channel = new TcpServerChannel(props, null);
// Register the channel
ChannelServices.RegisterChannel(channel, remotingSecure);
That basically says if the servers config setting "RemotingBindTo" is set then we bind to the provided IP address otherwise we bind to the machine name.
The problem with this is if you use a VPN connection to connect a client to the server. When the client is creating a proxy to a server object it is trying to connect via the machine name and the client PC can not resolve that to an IP address. A work around to this was to place the name into the Windows hosts file and tell it to resolve to the VPN IP address, but because the VPN gives a different IP address every time this is not a viable solution.
I suppose my question is: Is it possible AT ALL to get a .NET remoting server to bind to all available NICs/IP addresses?
Has anyone else experienced anything similar?

How do I start a tcplistener on a domain name?

I have the problem that my tcplistener only gets requests when specifying the actual IP in the browser, but I want to be able to call it by computer name as well.
var listener = new TcpListener(IPAddress.Any, 5556);
listener.Start();
visit this in browser:
http://mycomputername.com:5556/
but it doesnt work. I got my computer name by "nslookup [myip]"
Unless you are running your own internal DNS server the "mycomputername.com" url is going to get resolved by an external DNS server, which will return the external (internet facing) IP address. You will need to configure your router(s) to forward the correct port (tcp:5556) from the internet facing side of your network to the correct internal computer. This is usually in the "NAT", or "Firewall" section of your router's configuration.

How do I specify Server Ip address in my Client program?

I'm currently working with TCP/IP Sockets, My client console program has to connect with server which is my PC, If the destination in the client program is specified as a local host it works fine, what I need to get done with is to connecting the client with my server through internet, what I did is looked up for my IP address on http://www.whatismyip.com/ and tried but it didn't work as I think it's a Network Interface IP address, then I altered destination IP address in client program specifically to the address of my computer which I want it to be a server, but that didn't work also. Here's my code.
Ip = (IPAddress.Parse("192.168.1.4"));
MyClient.Connect(Ip,6000);
GetStream = MyClient.GetStream();
Console.WriteLine("CONNECTED TO SERVER");
Read = new BinaryReader(GetStream);
Write = new BinaryWriter(GetStream);
There 2 things (at least) that you should be aware of:
1. To access your computer from Internet (from the public address, the one you get with whatismyip.com) you need to open the port (6000) at the router, and tell the router to what IP it should forward the incoming connection. You could specified specifics ports or put a DMZ host where all the incoming connections will be routed to that host/PC. Read your router manual to see how that is done.
2. You cannot access your public IP from the inner side of the router (intranet), if you want to connect to your public IP you need to be in another network.
If you have a dynamic IP (is the default) every time the router is powered off and on then, most probably, that IP would change, you need to investigate thru whatismyip.com in order to know what IP has been assigned. You could connect to dyndns.org and ask for a host name myhost.mydomain.com (.es, .fr, etc), and in the router tell the DDNS (Dynamic DNS) to update that host every time the IP changes. In your client program you then connect to MyClient.Connect("myhost.mydomain.com", 6000);
Hope I explain myself well enough, anyway, if you have any question let me know.

How can i get IP Address of my 3G modem?

My GPRS modem has got a sim card. it can connect Web. Web service give it a ip number. i need it. like that: http://www.your-ip-address.com/
How can i do that C#?
You can use the static method WebClient.DownloadString(url) to read your external IP address from any web service providing such data:
string ip = System.Net.WebClient.DownloadString("http://whatismyip.org/");
If you are going to use this in a production environment, better make sure that the URL you are pointing to, is guaranteed to stay around for the entire lifespan of your application. The best way is probably to host the web service yourself.
Also, you should add some error checking around this code, as it will fail if the internet connection or the web service is unavailable.
You can get a list of your IP addresses via DNS using the following code:
var name = Dns.GetHostName();
var entry = Dns.GetHostEntry(name);
foreach (var address in entry.AddressList) {
Console.WriteLine(address);
}
If you want the IP address as a property of the hardware, you can use the System.Management.ManagementClass with the name Win32_NetworkAdapterConfiguration. See http://msdn.microsoft.com/en-us/library/system.management.managementclass.aspx for details.
You can create a WebRequest to http://whatismyip.com/automation/n09230945.asp which houses only your IP address
Start here

Categories

Resources