Can't connect to peer-to-peer NSNetService with BSD sockets

I am trying to connect two iPhones via peerToPeer WiFi connection. Both iPhones use NSNetService. One iPhone publishes the service and the other iPhone browses for the service.


Everything works great when I use streams similarly to WiTap sample. Both via peerToPeer WiFi and when both iPhones are connected to the same local network.


When I use BSD sockets (both for server and client) it works as long as both iPhones are connected to the same network. It never works peer-to-peer with BSD sockets. Client NSNetService resolves the server's service successfully to an IPv6 address but when I try to connect to it I always get a connection timeout.


Is it a known limitation? Any help will be highly appreciated!


Thanks.

Accepted Reply

I think you’ll find that the BSD Sockets client code will work correctly as long as you use the NSNetService server code. There’s some ‘special sauce’ being applied by the NSNetService server code that you can’t do yourself. Or at least that’s what I recall from the last time I looked into this.

I recommend that you run an experiment to confirm the above. If I’m right, you should feel free to file an enhancement request for BSD Sockets to provide the equivalent secret sauce. Please post your bug number, just for the record.

Having said that, this secret sauce only applies to the connection listening code. If you use NSNetService to listen for and accept the connection, you should be able to extract the BSD Socket from the resulting stream pair and then use BSD Sockets for the I/O part of your server.

Share and Enjoy

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

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

Replies

I think you’ll find that the BSD Sockets client code will work correctly as long as you use the NSNetService server code. There’s some ‘special sauce’ being applied by the NSNetService server code that you can’t do yourself. Or at least that’s what I recall from the last time I looked into this.

I recommend that you run an experiment to confirm the above. If I’m right, you should feel free to file an enhancement request for BSD Sockets to provide the equivalent secret sauce. Please post your bug number, just for the record.

Having said that, this secret sauce only applies to the connection listening code. If you use NSNetService to listen for and accept the connection, you should be able to extract the BSD Socket from the resulting stream pair and then use BSD Sockets for the I/O part of your server.

Share and Enjoy

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

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

I did run an experiment and unfortunately it didn't work for me. The netService:didAcceptConnectionWithInputStream:outputStream: delegate method is never called when client code tries to connect to the server using BSD sockets. I also ran the opposite experiment and it didn't work either. It only works for me (peer-to-peer) when both sides use NSNetService as in the WiTap sample code.


I will file an enhancement request as you suggested.


Thanks.

The [‘did accept connection’] delegate method is never called when client code tries to connect to the server using BSD sockets.

Weird. I presume that the client-side code resolved the service using

-resolveWithTimeout:
. Did you try connecting to all of the addresses returned by that?

Share and Enjoy

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

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

Yes I did.

Both iPhones are not connected to any WiFi network. I assume that's why I am always getting a single address which is an IPv6 address. I also checked to see if the `resolveWithTimeout` is called multiple times (in the aforementioned usecase), but is called only once (during the checks I didn't call service's `stop` method so the resolution process can continue).

Even if it worked, it doesn't really help me since I would have to modify my code in any case. I am working with an open source called CocoaAsyncSocket. Using streams for the server side means that I need to modify the open source to create a GCDAsyncSocket for an already connected socket that I got from the stream. Unfortunately, there is no such API in CocoaAsyncSocket so I either have to implement it or stop using CocoaAsyncSocket.

Unfortunately, there is no such API in CocoaAsyncSocket so I either have to implement it or stop using CocoaAsyncSocket.

Both of those options work for me (-:

  • In my experience the various third-party wrappers around NSStream don’t add much value. For example, if you want to integrate NSStream with GCD, you can use

    CFReadStreamSetDispatchQueue
    and
    CFWriteStreamSetDispatchQueue
    to do that.
  • OTOH, if you’re absolutely wedded to this third-party wrapper then it’s obviously worth the effort to add the ‘create from file descriptor’ functionality you need.

btw Most people who hit this restriction are using BSD Sockets, with some sort of huge legacy code base that assumes BSD Sockets. I assumed that was the case here as well, which is why my answers have been slightly off base.

Share and Enjoy

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

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

Thank you Quinn for your help.


I've filled an enhancement request. Bug number 32074207.