Internet connection with QUdpSocket
I recently programmed an instant messenger for local area networks. Now I would like to make it possible to chat over the internet, but I don't know exactly what to do.
Both clients are behind routers (home network), and I have no server so that it is not possible to make hole-punching.
Let's say I know the IP address of the router of the network I want to communicate with. Is it possible to make such a peer-to-peer-connection with Qt network classes (I prefer UDP sockets but wouldn't complain to use TCP)?
If computers are in a "local" network and connection to the Internet through a NAT router, their local IP addresses (192.168.x.y or 10.x.y.z) are NOT visible from outside the local network. For example: If you are located in some "local" network, the addresses in range 192.168.x.y or 10.x.y.z will refer to computers in your local network. If another user is located in a different "local" network, then the addresses in range 192.168.x.y or 10.x.y.z will refer to computers in his local network. The very same IP address may refer to completely different machine!
The one and only IP address that is "visible" from the Internet will be the public IP address that has been assigned to the NAT router when it connected to the Internet. The public IP is assigned by the ISP and usually will change after at most 24 hours. But: Most NAT routers have a feature called "port forwarding" or "virtual server". This allows to open a TCP/UDP port under the "public" IP address and thus make it available to the Internet. Incoming connections (on the public IP address) will then be forwarded to some local IP address of your choice.
With the "virtual server" approach you can get your "chat" application to work, given that at least one of the two (or more) users has configured his NAT router accordingly and thus can act as the server, i.e. one user will be able to accept incoming connections from the Internet. Still the other user(s), who will act as the client(s), and thus will connect to the server, will need to know the current "public" IP address of the user, who is acting as server. As said before, the public IP is assigned by the ISP and will change periodically.
If you need to connect to a server whose IP address will change, a service like "NO-IP":http://www.no-ip.com/ may be used.
BTW: Another solution would be to connect all clients through a VPN, such as "Hamachi":https://secure.logmein.com/products/hamachi/, first. Then all machines will act as if they were inside the very same "local" network (even if they are in different local networks) and most of your problems are gone. Just use the Hamachi addresses (5.x.y.z) to connect.
Clear so far. And is it also possible to make port forwarding automatic so that the users don't have to install it on their own? Maybe with UPnP?
I just want to add another option: use a standardized protocol which already offers NAT traversal, like XMPP, as implemented in for instance in "libpurple":https://developer.pidgin.im/.
[quote author="master of disaster" date="1356276043"]Clear so far. And is it also possible to make port forwarding automatic so that the users don't have to install it on their own?[/quote]
Totally depends on the individual router.
Routers for "home use" usually have a web-interface to configure "port forwarding". Should be easy enough for most "advanced" users (given they still remember the router password), but may be too "scary" for Average Joe.
And if you are in a "company" or "university" network (or something similar), you usually do NOT have access to the router configuration at all. Only the administrator might be able to re-configure the router...
[quote author="master of disaster" date="1356276043"]Maybe with UPnP?[/quote]
Maybe. Given the router happens to support UPnP. And given the UPnP support happens to be enabled.
But unless this is just for your own testing, you might be better off spending a few bucks for a server that's reachable from the Internet under a fixed public IP address and can initiate the connections between your clients.
(That's how Skype and Hamachi and all the others work to "bypass" NAT routers)
But how is it possible that the router accepts packets from a server, but not from another router? How is it even possible that you can use hole punching? How does the router know he should redirect the hole punched packets to my internal IP?
The local computer gets its "local" (non-public) IP address from the NAT router via DHCP. It also gets the subnet-mask and the Gateway IP-address that way. Via subnet-mask the local computer knows whether it can reach the target host (IP address) directly or not! If it can reach the target directly (because the target is within the same local network), things are simple. If it can not reach the target directly (e.g. target is on the Internet), it will send the request IP packet to the Gateway - which in this case is the NAT router - instead.
The NAT router does the following: It gets an (request) IP packet from a local machine. That IP packet has a "sender" and a "destination" IP address in its header. Initially the "sender" IP address is some local IP address (192.168.x.y) - the computer who sent that packet originally - and the "destination" is some public address on the Internet. Now the NAT router will overwrite the "sender" IP with its own public IP address. Then it simply sends the packet to the Internet via whatever Internet connection is being used.
The packet then goes to the destination server, it will be processed there and, eventually, the server sends an reply IP packet. Because the NAT router had overwritten the "sender" IP address (see above), the reply IP packet goes back to the NAT router's public IP address! Now the router does the processing in reverse direction: It receives the reply packet from the Internet, replaces the "destination" IP address (which currently is the NAT router's public address) with the local IP address of the local computer that originally sent the IP packet. Finally the reply IP packet goes to the local network and then to the actual receiver computer. Mission accomplished.
Now you may wonder: If the NAT router gets some IP packet from the Internet, how does it know to which local host the packet has to go? This is done via some "port number" magic, known as IP Masquerade! The router will set a unique port number for the outgoing packet and remember that port number - plus the local computer that port number belongs to. When an incoming packet is received, it will use the port number to figure out to which local computer it must forward the packet. Now it should be clear that this can only work, if the connection was initiated from a local computer! Or in other words: Only outgoing connections can "bypass" the NAT router. It's the NAT router's "internal" state (i.e. the "port-number to host" mapping) that allows reply-packets for an outgoing connection to be received/forwarded properly. But it cannot work with incoming connections! That's because if the connection wasn't initiated from the local network, the router can't know to which local computer the incoming packet is supposed to go (port number cannot be mapped). So the packet will simply be discarded...
...unless we have manually configured a "virtual server" (or "port forwarding") in the router setup menu!
OK, thanks! I have to send a packet to a server to receive packets from this server. Is this right, or is it only possible to receive one packet instead of holding a connection?
Once an "outgoing" connection has been established and thus the NAT router has set up the "port-number to host" mapping for the "reply" packets, IP packets can be transmitted in both directions. Note that the "reply" packets don't necessarily have to be sent from the server you originally connected to. Once the port is "open", the client (behind the NAT router) can receive IP packets from everybody who knows the right (public) IP address and the corresponding port-number. The server has that info and can share it. This way it is possible to use the server only for connection establishment, but then switch over to client-to-client communication.
Note also: Most communication is done via TCP these days. And TCP is session-based anyway! That's probably implementation specific, but I would assume the NAT router will "open" the port when a new (outgoing) TCP session is created (SYN flag is set) and will "close" it again when the TCP session is terminated (FIN flag is set) - or after a long time of inactivity. On the other hand, UDP is stateless (i.e. there is no "session"), so the port probably will be "opened" as soon as some outgoing UDP packet is seen and "closed" after a timeout.
Now it's absolutely clear! Thanks for your patience!