MLAPI.Puncher is a lightweight, cross-platform, easy to use, tiny implementation (<500 lines) of NAT punchthrough. It has both a server and client.
net35;net45;net471;netstandard2.0
)To start a server, use the MLAPI.Puncher.Server library. This library can run anywhere, for example as a console application (example in MLAPI.Puncher.Server.Console). To start the server, simply use:
PuncherServer server = new PuncherServer();
// 6776 is the port of the NAT server. Can be changed.
server.Start(new IPEndPoint(IPAddress.Any, 6776));
To use the client, you need the MLAPI.Puncher.Client library. This is what you use in your applications. An example of a console application can be found in MLAPI.Puncher.Client.Console.
The client has two parts, one part that is used by anyone who wants to allow other people to connect to them. This can be done like this:
// Creates the listener with the address and port of the server.
// Disposal stops everything and closes the connection.
using (PuncherClient listener = new PuncherClient("puncher.midlevel.io", 6776))
{
// 1234 is the port where the other peer will connect and punch through.
// That would be the port where your program is going to be listening after the punch is done.
listener.ListenForPunches(new IPEndPoint(IPAddress.Any, 1234));
}
Note that this will not return as it will continue to listen for punches. Its recommended to be ran in a thread. (If you want a method that exits, you can use listener.ListenForSinglePunch
which gives the EndPoint of the connector)
The second part is the connector. The party that wants to connect to a listener. It can be started with:
// Get listener public IP address by means of a matchmaker or otherwise.
string listenerAddress = "46.51.179.90"
// Creates the connector with the address and port to the server.
// Disposal stops everything and closes the connection.
using (PuncherClient connector = new PuncherClient("puncher.midlevel.io", 6776))
{
// Punches and returns the result
if (connector.TryPunch(IPAddress.Parse(listenerAddress), out IPEndPoint remoteEndPoint);
{
// NAT Punchthrough was successful. It can now be connected to using your normal connection logic.
Connect(remoteEndpoint);
}
else
{
// NAT Punchthrough failed.
}
}
If the connector is successful in punching through, the remote address and the port that was punched through will be provided in the out endpoint from the StartConnect method. If it failed, it will return false and the endpoint will be null.
The PuncherClient has a few settings that can be tweaked. They are listed below.
This is the transport you want to use, a transport is a class that inherits IUDPTransport and it's what handles all socket access. This allows you to integrate this into any networking library that has some sort of "Unconnected messages" functionality. It defaults to new SlimUDPTransport()
which is just a wrapper around the Socket class.
Port predictions are the amount of ports to be predicted, this is useful for solving symmetric NAT configurations that assigns ports in sequential order. What it actually does is, if you punch at port X and port prediction is set to 2. It will punch at X, X+1 and X+2.
This is the time the Connector will wait for a punch response before assuming the punchthrough failed.
This is the timeout for connecting to the Puncher server. If no response is received within this time, an exception will be thrown.
This is the interval at which a listener will resend Register requests. This is because servers will clear their records if a client is not sending pings regularly. Default is 60 seconds, default server clear interval is 120 seconds.
This is the timeout sent to the Transport Send methods. The default value is 500 milliseconds.
This is the timeout sent to the Transport Receive methods. You want to keep this fairly low as all timeouts depend on the receive methods returning rather quickly. The default value is 500 milliseconds.
If a connector sends a connect request to the Punch server, and gets a response that has a different address than the one we requested, and this option is turned on, the punch will be ignored. This could mean either that a proxy is used or that a routing attac is being performed. This defaults to true and is recommended to be set to true if you dont trust the punch server.
Both client and server has example projects. See MLAPI.Puncher.Server.Console and MLAPI.Puncher.Client.Console.
Currently, you can use the public punch server puncher.midlevel.io
on port 6776
.
This server has NO guaranteed uptime and is not recommended for production. Feel free to use it for testing.
Definitions:
LC = Listener Client
CC = Connector Client
PS = Puncher Server
Address = IPv4 Address WITHOUT Port
EndPoint = IPv4 Address AND Associated Port