I would like to set up a P2P matchmaking server. The situation is as follows:
- I have 2+ peers behind NAT'd router(s).
- I have a server behind a port-forwarded router on a static IP.
- The server's job is to register the contact information of each peer, and send it any other peer when requested.
- A peer could then query the server for a list of others, and directly connect to them.
Presently, peers can connect to the server, register their addresses, and query it for other peers' addresses. My issue is introducing the peers to each other.
I am presently getting the internal and external IP addresses of each peer like so. It is heavily based around the Master Server Sample that ships with Lidgren; however, that merely connects users to client/server sessions, while I require peer to peer.
//client code; getting our internal IP and sending it to the server
NetOutgoingMessage om = server_client.CreateMessage(); //create message on the server's connection
... //header
IPAddress mask;
var endpoint = new IPEndPoint(NetUtility.GetMyAddress(out mask), peer_client.Port); //gets the internal IP address
... //write message data and send to server
When the server gets a request, or when a peer is registered to the server, it gets that peer's external IP from the connection message. We then have access to both IPs from both peers when handling a request:
//server code, handling the peer request message:
...
string key = msg.ReadString();
Output("Requesting peer: " + key);
IPEndPoint requester_internal = msg.ReadIPEndPoint();
IPEndPoint requester_external = msg.SenderEndPoint;
//attempt to find requested user
User u = null;
if (Users.TryGetValue(id, out u))
{
IPEndPoint target_internal = u.IPInternal;
IPEndPoint target_external = u.IPExternal;
...
//send the requested IPs to the sender, and the sender's IPs to the target.
}
Now, the issue is how to connect the peers to each other using this information (or, whatever other information is required to make the connection), since the other infrastructure is already functional.
My first attempt was using the built-in Introduce method:
//server code, continuing from the above
//instead of directly sending IPs to users, introduce them ourselves
s_server.Introduce(target_internal, target_external, requester_internal, requester_external, key);
This seemed to work to some degree, but it connected to the server connection on each client instead of the peer connection, and only succeeded on the sender.
Attempting to manually connect with the sent IPs resulted in endless "attempting to connect" messages:
//peer code; handling 'other peer data received' msg
IPEndPoint peer_internal = msg.ReadIPEndPoint();
IPEndPoint peer_external = msg.ReadIPEndPoint();
Output("SERVER: Recieved peer IP. Attempting to connect to:\n" + peer_internal.ToString() + "\n" + peer_external.ToString());
NetOutgoingMessage om = peer_client.CreateMessage();
om.Write((Int32)3); //unique header for debugging purposes
peer_client.Connect(peer_external, om); //tried both internal and external, no dice
Lastly, a sample case of the IPs that I'm seeing, as dumped from the server:
P1 Internal: 169.xxx.xxx.xxx:14243 //local subnet address, using the proper port
P1 External: xxx.xxx.xxx.xxx:62105 //IP address of the system, matching that from any IP testing site, but using a seemingly random port
P2 Internal: 169.xxx.xxx.xxx:14243 //same as above for the second peer
P2 External: xxx.xxx.xxx.xxx:62106
I believe that the issue is that the peer connection's external port is randomly created somewhere along the line, instead of using that which I specify when creating it. As such, it can't be forwarded in advance. I would be happy with users being required to forward a specific port, as many P2P games work this way.
In other words, I seem to have everything that I need in order to get NAT punching working properly, but I lack the knowledge of Lidgren to make it happen.
Finally, here is how I set up my peer_client:
//peer code: initialize the connection to any other peers we may connect to later
NetPeerConfiguration pconfig = new NetPeerConfiguration(appidstr_client);
pconfig.Port = 14243; //properly forwarded in router
pconfig.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);
pconfig.SetMessageTypeEnabled(NetIncomingMessageType.UnconnectedData, true);
pconfig.AcceptIncomingConnections = true;
peer_client = new NetPeer(pconfig);
peer_client.Start();