CFReadStream & CFWriteStream callbacks not received

I want my application to use cellular data when WI-FI is On.

For that, I need access to socket before it is connected

so that it can be bound to cellular network IP and port.

Thought of using CFStreamCreatePairWithSocket() instead of CFStreamCreatePairWithSocketToHost()

but stream callbacks are not received.


Steps.

1. Create socket with CFSocketCreate() function

2. Get a native socket using CFSocketGetNative() and bind it to cellular IP.

3. Make a connection using CFSocketConnectToAddress() with timeout

parameter as -1 (Need a non-blocking connect).

4. Scheduled it over run loop using CFRunLoopAddSource

5. Upon kCFSocketConnectCallBack call back event create streams using

CFStreamCreatePairWithSocket().

6. Assigns clients to a streams for stream events.

7. Scheduled streams over run loop and opened the stream.


Expected Results:

1. Receive socket call back events.

2. Receive stream call backs events.


Actual Results:

1. Received kCFSocketConnectCallBack once and kCFSocketWriteCallBack twice. Rest events are not received.

2. Received only kCFStreamEventOpenCompleted.


Version/Build:

10.3.2


Kindly, let me know if I am missing something in above steps or there is any different way to do this.

Is there any API at application level instead of socket level to force application to use cellular network

when wifi is ON?

Accepted Reply

I recommend that you avoid using

CFSocket
here, because
CFSocket
has many sharp edges. The alternative is pretty simple: Do this part of the job using BSD Sockets. Now, BSD Sockets has lots of sharp edges as well, but they’re well-documented sharp edges (-:

The main complication with replacing

CFSocket
with BSD Sockets is the async connect. Fortunately dispatch event sources make that relatively easy.

Finally, once you have a connect BSD Socket I recommend that you wrap that in an

NSStream
pair rather than a
CFStream
pair, because the
NSStream
API is substantially easier to use. You do this by exploiting the toll-free bridge between the types, that is, call
CFStreamCreatePairWithSocket
and cast the resulting values to
NSInputStream
and
NSOutputStream
. You can steal the basic code outline from QA1652 Using NSStreams For A TCP Connection Without NSHost (although that uses
CFStreamCreatePairWithSocketToHost
instead of
CFStreamCreatePairWithSocket
).

Share and Enjoy

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

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

Replies

I recommend that you avoid using

CFSocket
here, because
CFSocket
has many sharp edges. The alternative is pretty simple: Do this part of the job using BSD Sockets. Now, BSD Sockets has lots of sharp edges as well, but they’re well-documented sharp edges (-:

The main complication with replacing

CFSocket
with BSD Sockets is the async connect. Fortunately dispatch event sources make that relatively easy.

Finally, once you have a connect BSD Socket I recommend that you wrap that in an

NSStream
pair rather than a
CFStream
pair, because the
NSStream
API is substantially easier to use. You do this by exploiting the toll-free bridge between the types, that is, call
CFStreamCreatePairWithSocket
and cast the resulting values to
NSInputStream
and
NSOutputStream
. You can steal the basic code outline from QA1652 Using NSStreams For A TCP Connection Without NSHost (although that uses
CFStreamCreatePairWithSocketToHost
instead of
CFStreamCreatePairWithSocket
).

Share and Enjoy

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

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

Thanks Quinn, using BSD socket worked in this scenario.

I had to wait on socket to become writable( using select() call) before opening the streams.
I will see if dispatch event sources make it more simpler to handle the async connect.


Looks like there is no way to select network interface at application level.

Hi Quinn,


Like kCFStreamNetworkServiceTypeVoIP, is there any setting needs to be done on BSD socket to ensure that sockets are not dropped by OS during backgrounding?


Regards

The legacy VoIP API (

kCFStreamNetworkServiceTypeVoIP
and friends) is not compatible with BSD Sockets. For it to function correctly you must use
CFSocketStream
(via either the
NSStream
or
CFStream
API) and use it asynchronously.

Share and Enjoy

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

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