UDP hole punching implementation in C#
UDP hole punching is a method that is used to establish connectivity between two hosts that are behind a NAT (router) without the need of having the clients port forward specific ports.
When the client sends a UDP packet to the server the router creates a temporary rule mapping, the server then uses the incoming IP address and port it receives the message from to send a message to the client, which refreshes (in most cases) the lifetime of the temporary mapping on the client’s router and allows the server to communicate constantly with the client without the need for the user to do anything from their side.
This method is also used to send messages between two users that are behind different NAT’s. The difference in the implementation is that the clients first contact with the server, which should have a port open in order to be able to receive the information from the clients. After the server receives the information from the clients it sends each client the IP and port of the other client, allowing both of them to communicate with each other.
I’ve created a small example on how the implementation of a UDP hole punching looks in C#, this should give you the general idea of how it works and let you expand it as you like. The example simply receives a message from the client then sends a response. If you want the server to handle more than one message then you will need to add some loops.
First the server side, which will receive the information from the client then respond.
Server:
1 2 3 4 5 6 7 8 9 10 11 12 | private void ServerForm_Load(object sender, EventArgs e) { int port = 27005; UdpClient udpListener = new UdpClient(port); IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port); byte[] receivedBytes = udpListener.Receive(ref ipEndPoint); // Receive the information from the client as byte array string clientMessage = Encoding.UTF8.GetString(receivedBytes); // Convert the message to a string byte[] response = Encoding.UTF8.GetBytes("Hello client, this is the server"); // Convert the reponse we want to send to the client to byte array udpListener.Send(response, response.Length, ipEndPoint); // Send the data to the client } |
Then a method will send a message of our choice to our server and return the response.
Client:
1 2 3 4 5 6 7 8 9 10 11 12 13 | private string SendMessageToServer(string message, IPEndPoint serverAddress) { string serverResponse = string.Empty; // The variable which we will use to store the server response using (UdpClient client = new UdpClient()) { byte[] data = Encoding.UTF8.GetBytes(message); // Convert our message to a byte array client.Send(data, data.Length, serverAddress); // Send the date to the server serverResponse = Encoding.UTF8.GetString(client.Receive(ref serverAddress)); // Retrieve the response from server as byte array and convert it to string } return serverResponse; } |
Client usage:
1 2 3 4 5 6 7 | IPAddress serverIP = IPAddress.Parse("xxx.xxx.xxx.xxx"); // Server IP int port = 27005; // Server port IPEndPoint ipEndPoint = new IPEndPoint(serverIP,port); string response = SendMessageToServer("hello server, this is the client", ipEndPoint); // Send the message to the server Console.WriteLine(response); // Output the result |
Hello, the tutorial is very very basic. Yet thanks for the tutorial. But why the author is silent in the question of P2P message? Many readers want to know this.
The following article is one of the most clarifying I’ve found on the issues expressed by some responses written here: http://bford.info/pub/net/p2pnat/
hello, i have small problem, only the server can send 1 message to the client after the client sends 1 message to the server. is it possible to have the server send messages to the client the same way the client can send them to the server? i had tried putting
which should have sent 5 messages back to the client but it only send one?
You will need to create a loop for the Receive method otherwise it will block until a message is received then stop listening.
Yah I am desperate for an example of how to then connect the 2 clients. I understand the process but Im just not sure how the server is supposed to proceed and open that door.
The server does not open the port.
The server is simply responsible to pass the information of host A (public IP and port) to host B and the information of host B to host A.
After that takes place then host A and host B simply exchange information between them without going through the server.
How is this done using TCP?
Thanks in advance :)
Hello, i have a question to ask about this scenario, i know that the server is responsible for passing information of client A and client B their ip and port. the issue i am having, isn’t client A and client B open ports only listening for messages from the server. or is this different between TCP and UDP. i had made a test project and sent it to a friend, they can communicate to the server and server can communicate back with Nat Punch, however all traffic is sent from clients to server then back out to all other clients. this is fine since clients then do not have other clients IP and port, safety for Doss attacks. but if i wanted to allow the users to communicate to each other without sending the data through the server and relaying back to the clients do i need to open another port or can they use the same port that was opened when they connected to the server. (i believe its known as the Punch part) please correct me if im wrong i am doing this as a learning exercise and an intro to networking with c# sockets. and would love to get more involved with Nat punch through implementation without having the users manually port forward.
Hi, thanks for this guide, it works well for me. The only problem I have is that once you have these two endpoints, how do you get them to connect to each other?
I’ve been stuck with this for a while now. For some reason sending to these endpoints, the clients just can’t connect. I will research more on NATs as my understanding is limited, just hope you could guide me in the right direction.
Thanks
I’m stuck at this point as well… After I get the IPs and Ports of both behind-NAT-clients, how do you connect each other? I tried to use the same method used to connect with the server, but didn’t work… even if I Close() the connection first. Also, how do I maintain the “temporary port” open and guarantee that is the one the socket will use to connect with the other client?
I was looking for an example on how to implemenent this, thanks
Great explanation, thank you for posting this