Search code examples
objective-ciosp2pgamekit

GameKit/Peer-to-peer over internet


For an iOS app I am developing, I want multiple phone to connect to each other and be able to voice chat between those devices.

I have it working when both devices are on the same network. This was quite simple and most of the stuff I want to do, is possible.

But now I am adding internet support, which is quite a hassle. I'll first try to explain how I want to match the devices, using a small webservice I set up.

Server

  1. Start a new GameKit session, with session-mode GKSessionModePeer
  2. Find the "Peer ID" of the server on the session I just created
  3. Create a new CFSocketRef on an free port and keep it ready to accept connections
  4. Send Peer ID and Port number to my webservice, running on an external server.

WebService

  1. Webservice receives the information and stores it together with an ID and the IP address of the client in a database.
  2. Send ID back to Server, which displays the ID

Client

  1. When the user chooses to use the "Online" feature of GameKit to search for games, I ask the user for an ID (where the user should input the ID the server receives).
  2. Client connects to the webservice supplying the ID. The webservice returns the information about the session (IP, PORT, Peer ID) of the server.
  3. The user tries to connect to the IP address, with the port information and set up an input and output stream with the server.

This does not work ofcourse, because my network does not allow incoming connections and a random port (from an external network).

But now the question is, how do I solve this? I want to be able to set up a peer to peer connection between 2 devices, those devices could be on the same network, but also on separate networks.

Is there a framework, example or anything showing how to do this? I want to be able to send data from device to device, without sending it to a server first.


Solution

  • I'm not aware of any frameworks that do this. I do however have a lot of experience with p2p networking across multiple networks.

    One important rule I learned: when communicating between networks, don't create a direct connection unless necessary. There are just too many factors that can (will?) cause issues, such as firewalls, NATs, etc.

    Sure, you can let the connection try first. You can try to connect to the given IP addresses*, but in most cases it will fail. Even when using UPnP and NAT-PMP, you'll find that in a lot of cases (more than half?) you won't be able to accept incoming connections at all.

    So make sure to have a backup plan. Make a network layer abstraction that doesn't only listen(), but also connects to a server. That way, when you can't connect to the IPs* of the client, you simply setup a connection via the server and the network abstraction takes care of it all.

    Let me reiterate the above: don't rely on incoming connections only, always have a backup plan.


    * I write IPs because clients can have multiple local/remote IPs. Always iterate over all these IPs when connecting. Example: my phone has 2 local IPv4 addresses (10.0.0.172 and 10.8.0.2), and an IPv6 address ([2001:x:x::6]). Of these three addresses, only the IPv6 address is publicly reachable, and the two IPv4 addresses are on different subnets so whether you can connect to them depends on the subnet that the other client is on. Always try to connect to both, and fall back to a server-proxied connection when it fails.

    ** I mentioned IPv6, yes. Let's not forget that IPv6 is not limited by NATs, unlike IPv4, and this means that you're far more likely to get a good connection via IPv6 than IPv4, if supported.