What is the proper API to use for UDP?

I am trying to figure out what is the recommended API to use for UDP networking on iOS.


In the Networking Programming Topics > Using Sockets and Socket Streams > Working with Packet-Based Sockets, it says that the recommended way is "by combining the POSIX API and either the

CFSocket
or GCD APIs."


However, in the Networking Overview > Avoiding Common Networking Mistakes > Avoid POSIX Sockets and CFSocket on iOS Where Possible, it says to not use POSIX sockets or CFSockets, because among other reasons it "does not automatically activate the device’s cellular modem or on-demand VPN". And also, from reading Supporting IPv6 DNS64/NAT64 Networks, it seems that those socket APIs would also not be compatible with DNS64/NAT64 whereas higher-level APIs would, even if given IPv4 addresses (e.g. "In iOS 9 and OS X 10.11 and later,

NSURLSession
and
CFNetwork
automatically synthesize IPv6 addresses from IPv4 literals locally on devices operating on DNS64/NAT64 networks.").


These seem like contradictory advice. I am wondering what is the current recommended API to use for UDP networking on iOS, and which, in particular, will automatically be compatible with on-demand VPN and DNS64/NAT64.

These are not contradictory given the presence of the “Where Possible” qualifier. In fact, that section of the docs (Avoid POSIX Sockets and CFSocket on iOS Where Possible) goes on to say that a key advantage of BSD Sockets is that you can “support protocols other than TCP”.

It’s generally true that, when you use UDP, you get greater flexibility but that comes at the cost of requiring your to do more work. iOS is no exception in this regard. iOS has lots of smarts in its connect-by-name subsystem, and UDP can’t take advantage of that because it doesn’t support a ‘connect’ operation.

I’ll address your specific concerns in the sections below.

With regards IPv6, there are no real obstacles to you supporting IPv6 in BSD Sockets-based UDP code. Your code won’t be as smart as the connect-by-name subsystem makes TCP, but it should work just fine.

IIRC the specific IPv6-related smarts that you miss out on are:

  • RTT-based address choice — If the DNS server returns multiple addresses for a given name, the connect-by-name subsystem uses round trip time information from the routing table to decide what address to try first. You don’t have access to the information needed to do that. Instead, you should do the standard BSD Sockets thing, that is, call

    getaddrinfo
    and use the addresses in the order that it returns.
  • try addresses until things work — If the DNS server returns multiple addresses for a given name, the connect-by-name subsystem will try all of the addresses until if finds one that works. This makes it very easy for the app to do the right thing in the presence of multiple IPv4 and IPv6 addresses, some of which might not worked in the current network environment (due to router misconfiguration, firewalls, and so on). BSD Sockets code has to do this retrying itself. For UDP this requirement is unavoidable because there’s no standard definition of works.

  • IPv4 literal addresses in NAT64 networks — As mentioned in the docs you quoted, this only works for high-level APIs. However, you shouldn’t be using literal IP addresses, but rather DNS, so this doesn’t matter.

With regards VPN On Demand, UDP’s lack of a ‘connect’ operation makes it impossible for it to integrate with the VPN On Demand system. This isn’t about APIs, but about the fundamental on-the-wire semantics of the protocol.

Note It’s important to realise that the VPN On Demand system is not traffic based. This was a deliberate design decision on our part, because traffic-based transient links tend to suffer from many false positives.

You can game the VPN On Demand system by using a dummy TCP connection to force the VPN to come up. I generally don’t recommend this (hey, I generally don’t recommend UDP!) but you wouldn’t be the first developer to go down that path.

Finally, I have to say that these problems are relatively minor compared to the issues required to make UDP work in general. There are lots of environments where using UDP anywhere outside of the local link just won’t work. For example:

  • anyone behind an HTTP proxy isn’t going to be able to use UDP

  • many corporate firewalls block UDP

  • NATs and UDP are not good friends; you can make them play well together in many, but not all, cases

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
What is the proper API to use for UDP?
 
 
Q