I've done some reading on UDP NAT traversal, and I'm reasonably confident I understand the basics but I'm still struggling with an implementation.
My project has an globally accessible server, and clients behind nat. Its a game, with the basic join_game request send from client to server, and the server then sends out updates every interval. I've been testing at home, and forgotten that I have DMZ on my router turned on so it worked fine. I sent this to some friends to test and they cannot receive updates from the server.
Here is the current methodology, all packets are UDP:
My understanding is the reply-to address that the server receives should have the correct port for traversing the client's nat. And sending packets down there often enough will keep nat traversal rule alive.
This is not happening. The client sends the join request, and receives the server's response on that socket. But when I close the socket then start up a threaded UDP listener on the reply-to port, it doesn't catch anything. Its almost as if the traversal rule is only valid for a single response packet.
I can include code if needed, but to be honest its several layers classes and objects and it does what I described above. The code works when I turn DMZ on, but not when its off.
I will include some snippets of interest.
Here is the server's handler for the join request. client_address is passed down from the threaded handler, and is the SocketServer.BaseRequestHandler attribute, self.client_address. No parsing, just passed down.
def handle_player_join(self, message, reply_message, client_address):
# Create player id
player_id = create_id()
# Add player to the connected nodes dict
self.modules.connected_nodes[player_id] = client_address
# Create player ship entity
self.modules.players[player_id] = self.modules.factory.player_ship( position = (320, 220),
bearing = 0,
)
# Set reply to ACK, and include the player id and listen port
reply_message.body = Message.ACK
reply_message.data['PLAYER_ID'] = player_id
reply_message.data['LISTEN_PORT'] = client_address[1]
print "Player Joined :"+str(client_address)+", ID: "+str(player_id)
# Return reply message
return reply_message
A friend has mentioned that maybe when I send the join request, then get the response I shouldn't close the socket. Keep that socket alive, and make that the listener. I'm not convinced closing the socket will have any effect on the nat traversal, and I don't know how to spawn a threaded udp listener that takes a pre-existing socket without rewriting the whole damn thing (which I'd rather not).
Any ideas or info required?
Cheers
You can do any one of two things to make your code work. They are,
Don't close the socket from which you have sent the packets to server. When you create a socket it binds to a private IP:Port. When you send a packet to server that IP:Port will be translated to your NATs one public IP:Port. Now when you close this socket then the data from your server comes first to your NATs public IP:Port and is forwarded to your private IP:Port. But as your socket is closed so no one will receive that data. Now the server has no way to know that you have created a new socket with new private IP:Port because you never sent a packet to your server after creating this new socket. So don't close the old socket. Try to listen with this old one in a thread. Or you can send a packet to the server from the new socket letting it know your new translatedpublic IP:Port. So that server can send its data to this new public IP:Port which will in turn be forwarded to your new private IP:Port.
Close the socket but reuse the same port. When you close your old socket and create your new socket, bind it to the port on which the old socket was bound. This will not change the NATs public IP:Port and data from your server will not be interrupted.