POSIX sockets and cellular modem

Hi!


The Networking Overview state that I should avoid POSIX sockets on iOS because it doesn't activate the cellular modem. At the same time it states that the "most appropriate times to use sockets directly are when you are developing a cross-platform tool ..."


I'm implementing a cross-platform library with a dependency on a third party MQTT-library that uses POSIX socket API (for TCP) , so for me there is a huge win in leveraging the POSIX APIs.


Is there any way to "manually" keep the cellular modem active when needed? Technically I guess I could just send some random data with a high level API to keep it active, the feels more like a hack. What is the right way to solve this issue?


Regards,

Markus

Replies

Historically the inability of BSD Sockets to activate WWAN was a major gotcha, which is why the documentation is littered with references to it. On modern systems, however, the WWAN network is pretty much pinned up by various system services (most notably push notifications).

OTOH, modern systems have added other reasons to use CFSocketStream, including:

  • VPN On Demand

  • IPv4 / IPv6 address selection

  • others than escape my mind at the moment

These require a connect-by-name API and there's no equivalent to that at the BSD Sockets level. Rather, BSD Sockets requires you to call

getaddrinfo
and then write a loop over each returned address, opening and connecting the socket, until you get one that works. Doing that reasonably well requires some ugly code, code that can’t be as good as the OS’s implementation ( this post describes some of the smarts used by the OS in this case).

One option in situations like this is to cut the ‘head’ off your POSIX sockets code and connect it to our connect-by-name API. That is:

  1. create a stream pair using

    +[NSStream getStreamsToHostWithName:port:inputStream:outputStream:]
  2. set

    kCFStreamPropertyShouldCloseNativeSocket
    to false (important because of step 7)
  3. schedule the streams on the run loop in a custom run loop mode

  4. call

    -open
  5. run the run loop in your custom run loop mode waiting for the streams to open

  6. extract the underlying socket file descriptor from the socket stream pair using

    kCFStreamPropertySocketNativeHandle
  7. close the streams

  8. return the file descriptor to your BSD Sockets based I/O code

This allows you to keep the bulk of your BSD Sockets code intact, while replacing the ugly bits of that code with ugly, but better, Apple specific code.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
  • +[NSStream getStreamsToHostWithName:port:inputStream:outputStream:]. is now deprecated, what should we do now? This permanent never ending deprecation without reasoning and giving the new recommendation in the documention is driving me crazy

Add a Comment

Hello,


I'd like to use a MQTT client in my application and I read this:

"If you are trying to use MQTT for an iOS application I will highly recommend you to use a native (Objective-C/Swift) iOS library. Using C or wrapper libraries usually means you are using POSIX networking calls at some point. Apple forbids the use of third party networking libraries from using the mobile internet antenna. Thus if you use Paho or something similar, you can only use MQTT when you are connected to a WiFi network."

So is it true and have I to avoid using c/wrapper lib?


Thanks,


Best regards,

Apple forbids the use of third party networking libraries from using the mobile internet antenna.

This is not true. There are numerous technical pitfalls with using BSD Sockets on our platforms, but there’s no outright ban on using that API to access WWAN.

[The only thing that comes close is on watchOS, where all networking must be done via higher-level protocols (HTTP[S] and Watch Connectivity). Third-party apps can’t open a ‘na‍ked’ TCP connection on watchOS. However, this limit applies equally to BSD Sockets and Apple-specific APIs, like NSStream.]

If you plan to use BSD Sockets on iOS you need to watch out for the following gotchas:

  • BSD Sockets has no connect-by-name mechanism, so you need to manually resolve the name (using getaddrinfo) and then connect to each returned address until you find one that works.

  • You must support IPv6, even if the server you’re talking to is IPv4-only (because of DNS64/NAT64).

  • For compatibility with DNS64/NAT64 you should not special case IP addresses. If you have an IP address, render it as a string and pass it to getaddrinfo.

    IMPORTANT You must not assume that getaddrinfo will return a single result if you pass it an IP address string.

  • You should implement Happy Eyeballs.

    Note I should stress that Happy Eyeballs will not work as well as the built-in connect-by-name mechanism used by Apple’s APIs. However, it’s probably good enough.

  • You will not be compatible with some features, like VPN On Demand.

Of these only the last one is Apple specific; everything is just the price you pay for using the ancient BSD Sockets API.


So, if your current MQTT library does all of the above correctly, I’d say go ahead and use it. If not, you have to choose between:

  • Fixing your current MQTT library

  • Finding a new MQTT library that uses BSD Sockets properly

  • Finding a new MQTT library that uses an Apple-specific TCP API

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"

Add a Comment

This came up in another context and I wanted to update my earlier post with some extra details. Earlier I wrote:

Note I should stress that Happy Eyeballs will not work as well as the built-in connect-by-name mechanism used by Apple’s APIs.

For information on what the built-in connect-by-name mechanism does beyond the traditional Happy Eyeballs algorithm, check out RFC 8305.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"

Earlier I wrote:

Third-party apps can’t open a ‘*****’ TCP connection on watchOS.

This has changed with watchOS 6. See this post for the details.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
@eskimo: i have encountered this problem myself too, with UDP sockets. Lost a week, just FYI. For completeness: I’m using setsockopt(IPPROTOIP/IPBOUND_IF) after doing the bind(...). This is a clear bug on iOS. That should have not been required. bind() is exactly for that: bind on an IP address. The only meaningful scenario would be when there are 2 or more interfaces having same ip, and bind() would not be enough to say which interface to use. Even then, metric of the interface should kick in and properly discriminate between those interfaces.
The moment I do that extra setsockopt everything works, modem is brought online.

also, this problem only happens on dual sim scenario. It works just fine without that setsockopt in single sim.

the way I see this is a clear POSIX API breakage, because sendto() should at very least return an error, not silently fail. The pkts are sent according to the pcap capture done on rvi0, so the iOS is contradicting itself, and that’s pretty bad.

As for pros and cons, it would be much better if we are let to decide if we go one route or the other. There is not a single place listed anywhere in the docs where this extra setsockopt is required. Do correct me if I’m wrong, it would be awesome to stand corrected.

overall, this breaks things...

the way I see this is a clear POSIX API breakage

In that case you should feel free to file a bug about that.

Please post your bug number, just for the record.

Keep in mind that BSD Sockets is a compatibility API on our platforms. The future of low-level networking is Network framework.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Somewhat off topic: do Apples guidelines/recommendations/rules prohibit use of IPv4 on a WiFi connection to an Access Point which is NOT connected to the internet?

Thannks

Somewhat off topic

Please start a new thread for this question.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"