I'm trying to add networking over the internet to my game, using Peer to Peer.
I have already implemented a LAN only version, which uses .NET's peer to peer classes to connect within the local network.
I realized I need to use UDP hole punching in order to establish a connection to other clients that are behind a firewall.
This is how I have started to implement it:
The server is a HTTP server using very simple PHP scripts to get the job done.
register name on server, sending the private ip address and port as a GET variable
when the server gets a registration request, it will save the public endpoint and the private endpoint
request to connect to another client.
the server will return the private and public endpoints of the client
every two seconds, the second client will check in with the server, and if there is another client attempting to connect with it, it will get its endpoints too
both clients start spamming each others public endpoints until they get a valid response
Now I have a bunch of questions
Can the server be a HTTP server?
When I make a request to an HTTP server, the client port changes each time a request is made. Is a private endpoint always matched to the same public endpoint?
Does the request to the server have to have the same source endpoint as the UDP messages to the client?
Is there a way to connect to an HTTP server in C# using a specified source endpoint?
Are there any problems with my implementation?
Thanks!
UDP hole punching is a complex topic, and many attempts have been made to find a solution that works. The problem is that there are so many different NAT routers out there, and there is no standard way of implementing NAT, so all routers behave a bit different.
Some attempts have been standardized, e.g. STUN, ICE. They still work only in most cases, not all, but they accumulate a lot of knowledge and address many concerns -- more than your attempt will ever be able to, simply because you can't test your solution with every NAT router under the sun. Skype, for example, spent years of perfecting its NAT traversal mechanism.
I recommend you have a look at STUN or preferably ICE. Try to implement one of them or look for existing implementations.
Another option might be to simply create a port forward at the router, e.g. using UPnP or NAT-PMP.
That said, I wouldn't be surprised if the .NET peer to peer classes came with a NAT traversal mechanism, but I'm not familiar with them.
STUN is a best solution for the problem. it works for most of the scenarios.here is a simple example(C#) that gives you the NAT type,Local IP:Port and Public IP:Port.
try
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
STUN_Result result = STUN_Client.Query("stunserver.org", 3478, socket);
Console.WriteLine("Net Type : " + result.NetType.ToString());
Console.WriteLine("Local IP : " + socket.LocalEndPoint.ToString());
if (result.NetType != STUN_NetType.UdpBlocked)
{
Console.WriteLine("Public IP : " + result.PublicEndPoint.ToString());
}
else
{
Console.WriteLine("");
}
}
catch (Exception x)
{
Console.WriteLine(x.StackTrace.ToString());
}
You can just add some breakpoints and check the behaviour of the code.
also you can traverse NAT using vs2010 onwords (.net4.0 < ) . there is a method AllowNATTraversal (bool value). set the value true for NAT Traverse. It uses Ipv6 address for connectivity. You can get Ipv6 global address from "Teredo" server by writing some commands in command prompt. Actually IPV6 is the technology that is used to avoid this problem.
Related
First of all I have to mention that I don't have any dedicated server in this case. But I know about the server's IP. So the problem is if the client connects to that IP, how could I know which client IP it uses? Because a client can have more than 1 network adapter, each one is connected to a different network. Also the server does not need to be in the same LAN with the client, it may require several hops before the client's packets reaching the server. We know the server's IP, how could we know the client's IP in this case?
Because we don't have dedicated server so we cannot detect the client's IP via some server app (some kind of clients manager). Also the Ping class seems not helpful in this case, also in general the server does not have to have pinging enabled. Currently the server is just a SQL Server to which the client executes some query directly. I know we can obtain the client's IP when it executes a query to my SQL Server (using sys.dm_exec_connections) but it's just the last resort for me. I would like to find another way directly from the client (and without involving the SQL Server).
Here is the closest approach I can follow but I don't really know why it chooses the last address?
var hn = System.Net.Dns.GetHostName();
var ipEntry = System.Net.Dns.GetHostEntry(hn);
IPAddress[] addr = ipEntry.AddressList;
var ip = addr[addr.Length - 1].ToString();//why the last?
Any way there are many IP addresses (not in the same LAN directly) which are not contained in the AddressList.
My problem is some kind of finding all possible network paths between the client (starting at some available network adapter on the client) and the server (with IP known beforehand). In almost cases there should be just 1 path available (so we should just find 1 client's IP). Trying sending some request from the client may be required to find the path but I currently don't have any clue on how it can be done.
You can easily do this from the client side using any available listening port on the server side (HTTP port, SSH port, or SQL Server port). Here's a small python program:
import sys
import socket
if len(sys.argv) != 3:
print("Usage: {} ip-or-hostname port-number".format(sys.argv[0]))
sys.exit(1)
hostname = sys.argv[1]
port = int(sys.argv[2])
s = socket.socket()
s.connect((hostname, port))
print(s.getsockname()[0])
What it does is to create a connection to the destination system, then uses the getsockname system call to provide the local address used in the connection. This local address will always correspond to one of your client side interfaces.
So for example, if I run it with arguments www.google.com 80, I get:
python /tmp/disc.py www.google.com 80
192.168.0.110
If I instead give it an address and port of a machine on a different local subnet, I get:
python /tmp/disc.py 192.168.40.128 22
192.168.40.131
The two addresses returned correspond to two different interfaces on my local system:
ip -4 addr
[snipped]
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.168.0.110/24 brd 192.168.0.255 scope global dynamic ens33
valid_lft 166968sec preferred_lft 166968sec
3: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.168.40.131/24 brd 192.168.40.255 scope global ens34
valid_lft forever preferred_lft forever
I'm sure you could easily implement the same program in C# -- just not a language I'm intimately familiar with.
Looks like I missed one important already built-in class to act as a client (we have HttpClient, WebClient ... but in this case I need a TcpClient). This can try connecting to some IP (with some port provided) and if it succeeds I can obtain the resolved LocalEndPoint of the underlying webSocket. It can just be converted to string but I can parse for the client IP it used (the string format is like IP:port). Here is just a few lines of code to achieve what I want:
public string GetIPConnectingTo(string targetIP, int port = 80){
try {
using(var tc = new TcpClient(targetIP, port)){
return tc.Client.LocalEndPoint.ToString().Split(':')[0];
}
}
catch {
return null;
}
}
In my program I am getting the local machine's public IP address like this
public static IPAddress getIPAddress()
{
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress addr in localIPs)
{
if (addr.AddressFamily == AddressFamily.InterNetwork)
{
return addr;
}
}
return null;
}
and it worked fine where I live.
Right now I am at a friend's house, and I am connected to internet via Wi-Fi, and this code does not give me my external IP address, it has probably something to do with the router settings, but I am not very familiar with networks...
The router is TP-LINK, and I can access its settings like this
By the way, the 8080 port is exactly the one I need, I only need to be able to access my public IP. How can I do it?
You can make a request to a site like http://icanhazip.com/ to get your external IP address
var request = WebRequest.Create("http://ipv4.icanhazip.com/");
var response = request.GetResponse();
var dataStream = response.GetResponseStream();
var reader = new StreamReader (dataStream);
string myIPAddress = reader.ReadToEnd();
More info here: https://msdn.microsoft.com/en-us/library/456dfw4f%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
There is no sure-fire way to do this that will work 100% of the time. However, there are two methods that will work most of the time.
The first is to use an external STUN server. It's relatively easy to add a configuration entry to your software to allow the user to change the STUN server if this server ever changes or goes down, they can choose another one.. there are many of them out there. A STUN server returns the users IP address back to the caller and is used by most VOIP devices.
The second is to use the built-in Universal Plug-n-Play framework (UPnP) to ask the router for its IP Address. This one, however, depends on the router supporting UPnP and it not being disabled. Additionally, UPnP may not work within a corporate network as there are several layers of routers and firewalls usually. Still, for home users this is typically a good option. There is a .NET based UPnP library here that utilizes the built-in COM based UPnP components in Windows:
http://managedupnp.codeplex.com/
I don't have any examples of how to implement this, but it supposedly has a good documentation library.
What you want is not possible. A router is a proxy of sorts, whereby all traffic must pass through it. The external IP address for all internal devices (including PCs, laptops, tablets, etc), will be the same--the internal IP address what is different. The only way for a device to know its external IP address is either to query the router (which might not be possible), or to query an external source. The port forwarding page you have in your post only shows the router where to redirect incoming traffic on that port, but it will tell the PC nothing as to what its external IP address is--because, generally, to the PC the external IP address is irrelevant.
Writing a chat program (as so many do) and i have found that i would like to be able to get the clients to connect to the server automatically.
However, the IP address of the server would not be permanent, so i cannot just hard-core it into the program
In TCP, I'm looking for some sort of broadcast feature, that allows the client to know where the server is
Any ideas?
EDIT: should have said, this will be a LAN program only - no outside connections
If you are talking about a chat in a LAN and you can't or don't want to use DNS for some reason, you could implement, or find an implementation of, the discovery protocol used by UPnP. The SSDP is based on a UDP broadcast. It is, afaik, not possible to multicast via TCP, because TCP needs a session.
If you want to use the chat server over the internet you have no choice but to use DNS. Look for a dynamic dns provider (I use selfhost.bz). In C# you can then resolve the hostname to an IP address as described in the other answers. If you have a hostname to connect to it will probably be enough to pass that to the socket, though:
socket.Connect("myhostname.selfhost.bz", ...
Edit: Since you say you're in a LAN, a few more details on SSDP. The protocol does way more, than you actually need. If you're thinking of implementing it yourself, don't stick to it exactly. Just make your clients send a broadcast on a specified port. The server permanently listens on that port, answering with a predefined message, once it receives a message. When the client receives that answer, it will know that the sender is a valid server.
Use DNS. Resolve the hostname in your app and connect to the IP it resolves to. You'll need dynamic DNS since you say the IP isn't permanent.
Use the below process to find server IP address
public string GetIPAddress()
{
string strHostName = System.Net.Dns.GetHostName();
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
return ipAddress.ToString();
}
You can also use
Request.ServerVariables["LOCAL_ADDR"];
I had an idea: just get the server to write the IP address/port/whatever to a textfile somewhere on the (public) network, and the clients can read the text file
Obviously, if the text file is not there or empty, no server is running...
Is this such a bad idea?
I am using TcpClient to listen on a port for requests. When the requests come in from the client I want to know the client ip making the request.
I've tried:
Console.WriteLine(tcpClient.Client.RemoteEndPoint.ToString());
Console.WriteLine(tcpClient.Client.LocalEndPoint.ToString());
var networkStream = tcpClient.GetStream();
var pi = networkStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance);
var socketIp = ((Socket)pi.GetValue(networkStream, null)).RemoteEndPoint.ToString();
Console.WriteLine(socketIp);
All of these addresses output 10.x.x.x addresses which are private addresses and are clearly not the address of the clients off my network making the requests. What can I do to get the public ip of the clients making the requests?
Edit:
We are using an Amazon EC2 Load Balancer with tcp forwarding. Is there a way to get the true client ip in this set up?
Does this work:
((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString()
If the client is connecting to you via an internal network I am not sure you can get their public IP since the connection to get back to the client would not need that information.
It sounds like perhaps your server is behind a load balancer or router using NAT. In this case, the IP packet won't have the originating client's address, but the address of the NAT router. Only the NAT router knows the sender's address (on an IP level).
Depending on whatever higher-level protocol you might be using on top of TCP, you may be able to get client identification from that, although it's much easier to spoof such information at higher levels, if that may be a concern.
If you need this data only for research purposes, your NAT device may keep a log.
If it's a requirement that you get the true originating IP packet in real time, you may have to have to reconfigure your router or have your server moved to the DMZ, but that's a whole nother ball of wax. Talk to your network guys, as they would certainly know more about this than I (I'm not a network expert).
Simply use the connection socket object of Socket class which you have used to accept the client.
connectionSocket.RemoteEndPoint.toString();
AdresseIP = DirectCast(SocketClient.Client.RemoteEndPoint, IPEndPoint).Address.ToString
I'm following a tutorial # http://www.geekpedia.com/tutorial239_Csharp-Chat-Part-1---Building-the-Chat-Client.html to try and gather the basics of networking. For those not wanting to hit the jump, it's a quick tut demonstrating how to program a simple client-server-model chat application.
When I try and run the code in the tut, it works fine as long as both the client and the server are on the same network, but the second I try and do it externally (getting a mate to run the client app, and running the server app my side), it all goes to pot. The fact that the code works when in the same network leads me to believe that it's not a coding issue, but an issue with the way my network is set up.
I'm trying to run the server on my IP address at port 21719, which I have opened, but still other people can't connect to my server, not able to get any form of response at all.
The code (from the tut) that is being used for the server to listen to connections is:
public void StartListening()
{
IPAddress ipaLocal = ipAddress; //ipAddress is parsed from txtIP
tlsClient = new TcpListener(ipaLocal, 21719);
tlsClient.Start();
ServRunning = true; //for the running loop
// Start the new tread that hosts the listener
thrListener = new Thread(KeepListening);
thrListener.Start();
}
Now, the tutorial does actually point out that
IPAddress ipaLocal = ipAddress;
Will cause issues on some configurations, and I'm beginning to fear that my configuration may be included in that.
So, does anyone have any solution for me?
Thanks,
Sam
What is the local IP address that you're using? (ipAddress) If it's 127.0.0.1, that's not correct (I don't know how it would work internally either, but Windows seems to use magic from time to time). Also, if you have multiple NICs in your local machine, maybe the port forwarding is only set up to forward to one of them, and you're using the IP of the other?
If that's not the problem, here are a few generic suggestions:
Grab a copy of netcat. It's a small network testing util whose only job is to form a simple TCP connection. That will allow you to eliminate your code as a variable in all this. If netcat can form a connection, then you know the problem is your code. If not, you've confirmed that it's your router.
You can use WireShark (or TShark) to look for ICMP packets. Capture ICMP packets on the remote machine. If you get "Destination Unreachable" from the router, you've again proved that it's your router.
As Spencer said you need to make sure Port Forwarding is setup on your router, to forward all packets that come in on port 21719 to your internal machine. As for exactly how to do that, it's hard to say without knowing what type of router.
Are you having people use your external (internet) IP address? (See yours here.)
Have you pinholed your router to forward all communications from port 21719 to your server?
Some tips:
What kind of operating system are you using? Please check the Scope and/or Profiles (under Advanced tab) of your firewall rule.
While your friend is trying to telnet to the port (connect to the im server) monitor the traffic using Wireshark or Network Monitor (Wireshark have problems with Vista and Win 7). If you don't see anything hitting your machine the problem is probably on the router side. Double check the settings - you said you set the forward rule (NAT) but did it also set the rule on firewall of your router?