I've got c# code running on a computer with multiple IP addresses, and I've got following code to select an IP address for a httpWebRequest:
class Interact
{
<data, cookies, etc>
HttpWebRequest CreateWebRequest(...)
{
.....
request.ServicePoint.BindIPEndPointDelegate = delegate(
ServicePoint servicePoint,
IPEndPoint remoteEndPoint,
int retryCount)
{
if (lastIpEndpoint!=null)
{
return lastIpEndpoint;
}
var candidates =
GetAddresses(remoteEndPoint.AddressFamily);
if (candidates==null||candidates.Count()==0)
{
throw new NotImplementedException();
}
return
lastIpEndpoint = new IPEndPoint(candidates[rnd.Next(candidates.Count())],0);
};
};
return request;
}
}
Here's the code of GetAddresses:
static IPAddress[] GetAddresses(AddressFamily af)
{
System.Net.IPHostEntry _IPHostEntry = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
return (from i in _IPHostEntry.AddressList where i.AddressFamily == af select i).ToArray();
}
This code is supposed to select a random IP from avaliable IP list, and than stick to it.
Instead, every time I send request with it, I'm getting following exception:
Unable to connect to the remote server
How do I make this work?
It looks like you are setting the port number of the end point to zero in the line:
lastIpEndpoint = new IPEndPoint(candidates[rnd.Next(candidates.Count())],0);
Unless this gets changed later on, it is unlikely that you will be able to connect to an HTTP server on port 0. You may be able to use the port contained in the remoteEndPoint, or perhaps you can hard code the port number if it is well known (eg, 80 for HTTP server running on default port).
lastIpEndpoint = new IPEndPoint(candidates[rnd.Next(candidates.Count())], remoteEndPoint.Port);
Related
I'm trying to get the user's IP address from ASP.NET MVC 5. I've looked up various examples, such as these:
https://stackoverflow.com/a/740431/177416
https://stackoverflow.com/a/20194511/177416
https://stackoverflow.com/a/3003254/177416
They've all produced the same result: the user is considered internal to the network. I've had friends try their phones (which are not on the network). Here's my latest attempt:
private static Logger _logger = LogManager.GetCurrentClassLogger();
public static bool IsIpInternal()
{
var ipAddress = HttpContext.Current.Request.UserHostAddress;
var logEvent = new LogEventInfo(LogLevel.Info, _logger.Name, ipAddress);
_logger.Log(logEvent);
try
{
if (ipAddress != null)
{
var ipParts = ipAddress.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse).ToArray();
var isDebug = System.Diagnostics.Debugger.IsAttached;
if (ipParts[0] == 10)
{
return true;
}
}
}
catch (Exception e)
{
logEvent = new LogEventInfo(LogLevel.Error, _logger.Name, e.Message);
_logger.Log(logEvent);
return false;
}
return false;
}
The log is showing 10.xxx.xx.xxx for all requests (based on the log). This is an internal address rather than the IP of the client connecting to the web app. The IsIpInternal() returns true always. What am I doing wrong?
Note that I'm ignoring 192.168.x.x and 172.16.xxx.xxx addresses as being internal.
If your web site is behind a load balancer, it is a common problem for the load balancer's IP address to appear when you are expecting the client's IP address. That is because in reality the load balancer is the only client that the web application knows is talking to it.
There are two ways to deal with this:
You can configure the load balancer to add an additional HTTP header (x-forwarded-for) that specifies the original IP address. You will need to modify your web site to look at this header instead of the UserHostAddress, like this:
//var clientIP = HttpContext.Current.Request.UserHostAddress;
var clientIP = HttpContext.Current.Request.Headers["x-forwarded-for"];
Note: The x-forwarded-for header can actually return a comma-delimited list of IP addresses in some cases. So to be compatible with such an occurence, you might write this instead:
var clientIP = HttpContext.Current.Request.Headers["x-forwarded-for"].Split(',')[0];
You can configure certain LBs to pass through the client IP by copying the IP header packet. For Citrix Netscaler, see this article for more information.
I created a multithreaded client-server chat application and want to test my application with multiple clients. I'm planning to create a simulator in client side which create a random port and IP. By that I mean my client system should run with multiple ports (without running multiple times).
I tried to find out the part of the code which gives client IP and port number in the client class, but couldn't figure it out. I only found the part which gives server IP and port.
This is my connection establishing part
private void cmdConnect_Click(object sender, System.EventArgs e)
{
try
{
//create a new client socket ...
m_socWorker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
String szIPSelected = txtIPAddress.Text;
String szPort = txtPort.Text;
int alPort = System.Convert.ToInt16 (szPort,10);
System.Net.IPAddress remoteIPAddress = System.Net.IPAddress.Parse(szIPSelected);
System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(remoteIPAddress, alPort);
m_socWorker.Connect(remoteEndPoint);
}
catch (System.Net.Sockets.SocketException se)
{
MessageBox.Show ( se.Message );
}
}
and my data sending part
private void cmdSendData_Click(object sender, System.EventArgs e)
{
try
{
Object objData = txtData.Text;
byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString ());
m_socWorker.Send (byData);
}
catch(System.Net.Sockets.SocketException se)
{
MessageBox.Show (se.Message );
}
}
If you really need to set the client socket address before making a connection to the server, and it is often a valid scenario when one wants to force the kernel to bind to a specific local address on multihomed systems, you may call Socket.Bind() before you call Socket.Connect():
IPAddress localIPAddress = IPAddress.Parse(szLocalIP);
IPEndPoint localEndPoint = new IPEndPoint(localIPAddress, 0);
m_socWorker.Bind(localEndPoint);
m_socWorker.Connect(remoteEndPoint);
You may also explicitly specify the local port number in the IPEndPoint constructor, but you have to make sure, that the port is not in use. If you leave the port number equal to 0, then the system would pick an (almost) arbitrary free port number.
Note that you cannot just pick an arbitrary client IP address - it must be one of the addresses, configured on your system's enabled network interfaces. See the output of ipconfig /all for what is configured. You can assign more than one IP address to an interface if you simply want to test.
I'm having a bit of trouble in getting a very simple TCP client working on my HTC Titan w/ Windows Phone 7.5.
When the USB cable is attached to the phone, the TCP client works like a charm, but as soon as the cable is unplugged, the client is unable to connect to a TCP server running on my development machine. The devices are on the same network and I'm using the explicit IP-address of my desktop machine to connect, so there's no name resolution going on afaik.
Here's the code I use. Most of it was taken from the Sockets samples on MSDN (can't seem to find the link now though).
private Socket _sock = null;
private ManualResetEvent _done = new ManualResetEvent(false);
private const int TIMEOUT = 5000;
//connect to server
public string Connect(string ip, int port) {
string result = string.Empty;
var host = new IPEndpoint(IPAddress.Parse(ip), port);
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sock.SetNetworkRequirement(NetworkSelectionCharacteristics.NonCellular);
var args = new SocketAsyncEventArgs();
args.RemoteEndPoint = host;
args.Completed += new EventHandler((s,e) => {
result = e.SocketError.ToString();
_done.Set();
});
_done.Reset();
_sock.ConnectAsync(args);
_done.WaitOne(TIMEOUT);
return result;
}
//send message
public string Send(string msg) {
string response = "Operation timeout";
if (_sock != null) {
var args= new SocketAsyncEventArgs();
args.RemoteEndPoint = _sock.RemoteEndPoint;
args.Completed += new EventHandler(s, e) => {
response = e.SocketError.ToString();
_done.Set();
});
var payload = Encoding.UTF8.GetBytes(data);
args.SetBuffer(payload, 0, payload.Length);
_done.Reset();
_sock.SendAsync(args);
_done.WaitOne(TIMEOUT);
}
return response;
}
//receive message
public string Receive() {
string response = "Operation timeout";
if (_sock != null) {
var args= new SocketAsyncEventArgs();
args.RemoteEndPoint = _sock.RemoteEndPoint;
args.SetBuffer(new Byte[MAX_BUFSIZE], 0, MAX_BUFSIZE);
args.Completed += new EventHandler((s,e) => {
if (e.SocketError == SocketError.Success) {
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
}
else {
response = e.SocketError.ToString();
}
_done.Set();
});
_done.Reset();
_sock.ReceiveAsync(args);
_done.WaitOne(TIMEOUT);
}
return response;
}
The code is then simply used like:
Connect(...);
Send(...);
Receive(...);
//and then close the socket
As I said before, the code works like a charm when the device is attached to my development machine. When the cable is unplugged, the connection phase just times out (regardless of the timeout interval I should say).
Also, the manifest contains the ID_CAP_NETWORKING capability which as I understand it should give the app permission to access the network.
Any ideas?
EDIT:
I discovered that switching to UDP communication works like a charm. Which means that the problem is that for some reason, the phone is unable to set up a persistant TCP connection to my dev machine. This is getting stranger by the minute.
Do you have a wireless ap nearby on which your phone is connected? because when you plug it in the pc it uses the pc's network connection.
You should check the IP address that you have on both the phone (from your code) and on the PC (which it looks like you've already found using ipconfig in your command prompt).
These should be in the same IP address range, and so start with the same digits (for IPv4, probably something link 192.168.0.*).
If this all matches up check your wireless router hasn't enabled a security setting which means that it doesn't allow TCP traffic from your phone to your PC.
If this is a consumer router you manage this should be fairly simple to verify (and potentially fix). If not, you're probably stuck...
I have a problem with configuration and starting WCF service.
In my application there is a method that starts service. Something like this
void Start(string protocol, string address, string port)
{
host = new ServiceHost(_myService,
new Uri(String.Format("{0}://{1}{2}/Sample", protocol, address, port)));
//...Some configuration (bindings, behaviors, etc.)
host.Open();
}
Let my computer has an IP 192.168.0.1. When I pass 'address' parameter with a value '192.168.0.2' an error occurred
"A TCP error (10049: The requested address is not valid in its context.)
occurred while listening on IP Endpoint=192.168.0.2:1234"
That's right because it's not my IP. But after that if I pass correct value (my real IP) I get the same error about IP 192.168.0.2! So I can't reconfigure and restart server without restarting application.
Why does it happen? How can I avoid such behavior?
Looks like WCF caches a socket object in class ExclusiveTCPTransportManager.
It's seems to be good solution for me:
public static class WCFBugWorkaround
{
public static bool IsConnectionPossible(this ServiceHost host)
{
try
{
foreach (var baseAddress in host.BaseAddresses)
{
IPAddress[] ipAddresses = Dns.GetHostAddresses(baseAddress.DnsSafeHost);
IPAddress ipAddr = ipAddresses.Where(e => e.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault();
if (ipAddr == null)
{
return false;
}
using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
System.Net.IPEndPoint localEP = new IPEndPoint(ipAddr, baseAddress.Port);
s.Bind(localEP);
}
}
}
catch (Exception ex)
{
return false;
}
return true;
}
}
ServiceHost host = ...;
...
if (host.IsConnectionPossible())
{
host.Open();
}
Thank's to Francheska for showing me the right direction :)
I can't see from your question how you are adding the correct endpoint, but I suspect you are attempting to modify the endpoint address. With WCF services, you cannot make changes to the endpoint address after calling
host.Open();
because at this point the service is (if you have no errors) up and accepting requests from clients at the specified address and port number.
You need to create a new ServiceHost object with the correct endpoint address (and dispose of the old one) if you wish to host the service at a new address.
EDIT:
After playing around with the example solution you have posted, I have found a solution to the issue. I think something is going wrong because you are using the same port number for both tries (in the example solution I downloaded you don't specify this, so the port defaulted to 808). The error you are experiencing vanishes if you change your code as follows to specify a different port number in the base address for the 2nd try:
try
{
var host2 = CreateServiceHost("localhost:5432", serviceImpl);
Console.WriteLine("#2, config: " + host2.BaseAddresses.First().ToString());
host2.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
There seems to be something buggy underneath WCF itself, probably on a socket level, where the port is somehow still unavailable after the first error with the incorrect IP.
I did a quick google and found this article where someone experienced a delay in reusing a port after closing a socket. If you always need to use the same port number, perhaps you could wait a certain amount of time for the port to become free again before trying to create the service host again.
The exception is Remoting Exception - Authentication Failure. The detailed message says "Unable to read data from the transport connection: the connection was closed."
I'm having trouble with creating two simple servers that can comunicate as remote objects in C#. ServerInfo is just a class I created that holds the IP and Port and can give back the address. It works fine, as I used it before, and I've debugged it. Also the server is starting just fine, no exception is thrown, and the channel is registered without problems. I'm using Forms to do the interfaces, and call some of the methods on the server, but didn't find any problems in passing the parameters from the FormsApplication to the server when debugging. All seems fine in that chapter.
public ChordServerProgram()
{
RemotingServices.Marshal(this, "PADIBook");
nodeInt = 0;
}
public void startServer()
{
try
{
serverChannel = new TcpChannel(serverInfo.Port);
ChannelServices.RegisterChannel(serverChannel, true);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
I run two instances of this program. Then startNode is called on one of the instances of the application. The port is fine, the address generated is fine as well. As you can see, I'm using the IP for localhost, since this server is just for testing purposes.
public void startNode(String portStr)
{
IPAddress address = IPAddress.Parse("127.0.0.1");
Int32 port = Int32.Parse(portStr);
serverInfo = new ServerInfo(address, port);
startServer();
//node = new ChordNode(serverInfo,this);
}
Then, in the other istance, through the interface again, I call another startNode method, giving it a seed server to get information from. This is where it goes wrong. When it calls the method on the seedServer proxy it just got, a RemotingException is thrown, due to an authentication failure. (The parameter I'll want to get is the node, I'm just using the int to make sure the ChordNode class has nothing to do with this error.)
public void startNode(String portStr, String seedStr)
{
IPAddress address = IPAddress.Parse("127.0.0.1");
Int32 port = Int32.Parse(portStr);
serverInfo = new ServerInfo(address, port);
IPAddress addressSeed = IPAddress.Parse("127.0.0.1");
Int32 portSeed = Int32.Parse(seedStr);
ServerInfo seedInfo = new ServerInfo(addressSeed, portSeed);
startServer();
ChordServerProgram seedServer = (ChordServerProgram)Activator.GetObject(typeof(ChordServerProgram), seedInfo.GetFullAddress());
// node = new ChordNode(serverInfo,this);
int seedNode = seedServer.nodeInt;
// node.chordJoin(seedNode.self);
}
Try setting the ensureSecurity to false, and it should start working.
ChannelServices.RegisterChannel(serverChannel, false);
You've specified that security is a must on your Remoting server in startServer() with:
ChannelServices.RegisterChannel(serverChannel, true);
Yet the 'client' end does not specify security, hence the authorisation error. You need to specify tcp channel security on both ends unless the server security setting is set to 'false'. In your second startNode method you need to do the following before using Activator.GetObject, note no port specified on the TcpChannel unlike the server end:
TcpChannel ClientChan = new TcpChannel();
ChannelServices.RegisterChannel(ClientChan, true);
Furthermore, unless you're doing it in some code you haven't given us, you also do not seem to have registered a well known service type server side, although you say it's been working in the debugger so maybe that's not necessary in the case. See MSDN on RegisterWellKnownServiceType.