Multiple incoming connections on NWListener for single connection attempt

I am using NWListener and NWBrowser to establish a NWConnection between two local devices. I noticed that if I run the NWBrowser client on the iOS Simulator and create a (single) connection for a result endpoint found by the browser, the NWListener's newConnectionHandler on the destination device is called with three different connections.

Is this a bug or just something unique to the simulator? Or should I prepared to handle this in general? And if so, should I just accept the first connection and reject subsequent ones, or pick the last one, or choose the best option amongst them (and if so, how)?

Here is an example of the three connections received by the NWListener for the one NWConnection attempt from the simulator:

Client connected: [C1 fe80::8bb:e5ff:fe18:bab%anpi0.57383 tcp, traffic class: 700, local: fe80::8bb:e5ff:fe18:b54%anpi0.56395, definite, attribution: developer, server, path satisfied (Path is satisfied), viable, interface: anpi0, scoped]

Client connected: [C2 fe80::53:558d:8f79:9a0c%en2.57384 tcp, traffic class: 700, local: fe80::897:b297:76e0:a53%en2.56395, definite, attribution: developer, server, path satisfied (Path is satisfied), viable, interface: en2, scoped]

Client connected: [C3 fe80::3c:91d7:b7e:7c2b%en0.57385 tcp, traffic class: 700, local: fe80::1cf2:d1b6:220d:4d8f%en0.56395, definite, attribution: developer, server, path satisfied (Path is satisfied), viable, interface: en0, scoped, ipv4, ipv6, dns]

Accepted Reply

It’s very common for a listener to receive multiple redundant connections due to the the client’s Happy Eyeballs implementation. My advice is for you to accept them all. The client will then drop all except the one that it wants to use.

From the listener’s perspective this is no different from you ignoring connections that are dropped immediately, and your listener has to do that anyway.

Share and Enjoy

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

  • Will do! Thank you for the fast and helpful reply!

  • What does 'dropped immediately' mean? I am now calling start() on all incoming connections and was expecting that only one of them would enter the .ready state. But they all do. So do I need to send my own handshake packet from the client to identify which connection is the real one? Or just wait for a short amount of time? In my app I want to establish one working connection, and then shut down the Bonjour listener and browser and start sending video data across the connection.

Add a Comment

Replies

It’s very common for a listener to receive multiple redundant connections due to the the client’s Happy Eyeballs implementation. My advice is for you to accept them all. The client will then drop all except the one that it wants to use.

From the listener’s perspective this is no different from you ignoring connections that are dropped immediately, and your listener has to do that anyway.

Share and Enjoy

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

  • Will do! Thank you for the fast and helpful reply!

  • What does 'dropped immediately' mean? I am now calling start() on all incoming connections and was expecting that only one of them would enter the .ready state. But they all do. So do I need to send my own handshake packet from the client to identify which connection is the real one? Or just wait for a short amount of time? In my app I want to establish one working connection, and then shut down the Bonjour listener and browser and start sending video data across the connection.

Add a Comment

I am still not quite sure about the correct way to implement this. I want to establish a single working connection between my two devices and then shut down the NWListener and start using the established connection in my app. I was expecting only one of the incoming connections to enter the .ready state after accepting them, but they all do. They are also all reported as viable. My current solution is to send an initial message from the client over the connection and try to read from all the connections returned by the NWListener (once they are .ready ), and use the one connection through which I receive this message. This does work. But the warning messages the Network Framework prints to the console make me feel like I am doing it wrong?:

[connection] nw_flow_add_read_request [C2 fe80::53:558d:8f79:9a0c%en2.60902 ready channel-flow (satisfied (Path is satisfied), viable, interface: en2, scoped)] already delivered final read, cannot accept read requests
[connection] nw_read_request_report [C2] Receive failed with error "No message available on STREAM"
Error receiving next message: POSIXErrorCode(rawValue: 96): No message available on STREAM.

I am calling receiveMessage() on the connection immediately after it enters the .ready state.

Is there a better way? Maybe wait for some amount of time, during which all but one connection should change from .ready to .cancelled? But what is the shortest time threshold that is guaranteed to be long enough?

But the warning messages the Network framework prints to the console make me feel like I am doing it wrong?

I wouldn’t worry about that. Network framework does a lot of logging and that’s exactly what I’d expect to see if I cancelled a connection with an outstanding read.

Share and Enjoy

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