I'm not native to networking so maybe I'm misunderstanding how endPoint information is gathered.
Device_A is browsing and discovers Device_B or the other way around, it doesn't matter because they will both discover each other and send data to open a connection. Because the Network framework does not resolve ipAddresses, when a connection is first made I use either the remoteEndPoint (connection.currentPath?.remoteEndpoint // fe80::9821:7fff:fcea:74c4%awdl0.10059) or the endPoint description (connection.endpoint.debugDescription // myApp (2)._myApp._tcplocal.) as a uniqueID for the connection.
I send the data and some other info across the wire, if successful I then place either endPoint inside an ivar dictionary as the key with another value so that I know which device to map a response to once a device responds back.
Right now I only have 2 devices connected, the issue is when I receive a response from Device_B, I'm actually getting the incorrect endPoint information. For some reason I keep getting the endPoint info from Device_A back. It seems like the same endPoint information is getting sent twice. Once to Device_A and then again to Device_B. This exact same thing occurs on Device_B but in reverse. I'm confused as to why this is happening.
For example Device_A first discovers itself, the remoteEndPoint is *fe80::9821:7fff:fcea:74c4%awdl0.10059*, it sends the data. When Device_A receives its own message, I filter it out using the userId and I see the same endPoint info. But when Device_A discovers Device_B, the remoteEndPoint is *fe80::9821:7fff:fcea:74c4%awdl0.27788*. When I receive a response from Device_B, the endPoint information is showing the first one from Device_A *fe80::9821:7fff:fcea:74c4%awdl0.10059*. The same remoteEndPoint info is duplicated. The same exact thing is happening if I use endpoint.debugDescription. This issue occurs on both devices.
NWBrowser:
PeerConnection:
Send Data:
Receiving a response
[1]: https://developer.apple.com/forums/thread/129644
Device_A is browsing and discovers Device_B or the other way around, it doesn't matter because they will both discover each other and send data to open a connection. Because the Network framework does not resolve ipAddresses, when a connection is first made I use either the remoteEndPoint (connection.currentPath?.remoteEndpoint // fe80::9821:7fff:fcea:74c4%awdl0.10059) or the endPoint description (connection.endpoint.debugDescription // myApp (2)._myApp._tcplocal.) as a uniqueID for the connection.
I send the data and some other info across the wire, if successful I then place either endPoint inside an ivar dictionary as the key with another value so that I know which device to map a response to once a device responds back.
Right now I only have 2 devices connected, the issue is when I receive a response from Device_B, I'm actually getting the incorrect endPoint information. For some reason I keep getting the endPoint info from Device_A back. It seems like the same endPoint information is getting sent twice. Once to Device_A and then again to Device_B. This exact same thing occurs on Device_B but in reverse. I'm confused as to why this is happening.
For example Device_A first discovers itself, the remoteEndPoint is *fe80::9821:7fff:fcea:74c4%awdl0.10059*, it sends the data. When Device_A receives its own message, I filter it out using the userId and I see the same endPoint info. But when Device_A discovers Device_B, the remoteEndPoint is *fe80::9821:7fff:fcea:74c4%awdl0.27788*. When I receive a response from Device_B, the endPoint information is showing the first one from Device_A *fe80::9821:7fff:fcea:74c4%awdl0.10059*. The same remoteEndPoint info is duplicated. The same exact thing is happening if I use endpoint.debugDescription. This issue occurs on both devices.
NWBrowser:
Code Block browser.browseResultsChangedHandler = { (results, changes) in for change in changes { switch change { case .added(let browseResult): switch browseResult.endpoint { case .service(let name, let type,_,_): let connection = PeerConnection(to: browseResult.endpoint) // ... }
PeerConnection:
Code Block var connection: NWConnection? init(to endPoint: NWEndpoint) { // tcpOptions ... // params ... // initialize connection and delegate that sends out data connection.stateUpdateHandler = { (nwConnectionState) in case .ready: self.delegate.sendOutgoing(connection) }
Send Data:
Code Block var dict = [String:String]() var endPointArr = [String]() func sendOutgoing(_ connection: NWConnection) { let endPoint = connection.currentPath?.localEndpoint?.debugDescription or let endPoint = connection.currentPath?.remoteEndpoint?.debugDescription // encode the endPoint and currentUserId with some other info, then send it across the wire, if successful make the endPoint a key inside a dictionary and add it to an array to keep track of what endPoints were received connection.send(content: encodedData, contentContext: context, isComplete: true, completion: .contentProcessed({ [weak self](error) in { if let error = error { return } self?.dict[endPoint] = someUniqueValue self?.endPointArr.append(endPoint) })) }
Receiving a response
Code Block connection.receive(minimumIncompleteLength: 1, maximumLength: 65535) { [weak self](data, context, isComplete, error) { if let err = error { return } if let data = data, !data.isEmpty { self?.received(data) } } func received(_ data) { guard let retrievedData = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? MyModel else { return } guard let endPoint = retrievedData.endPoint as? String, let userID = retrievedData.userId as? String else { return } print(endPoint) // fe80::9821:7fff:fcea:74c4%awdl0.10059 if userID == Auth.auth().currentUser?.uid { dict[endPoint] = nil return } endPointArr.forEach { (endPoint) in print(endPoint) // prints both fe80::9821:7fff:fcea:74c4%awdl0.10059 and fe80::9821:7fff:fcea:74c4%awdl0.27788 } // this never runs because the key just got set to nil above because Device_B has the same endPoint info for (key, value) in dict where key == endpoint { print("key=\(key) : value=\(value)") // someUniqueValue // show response that this is a response from whichever device has this endPoint break } }
[1]: https://developer.apple.com/forums/thread/129644
I suspect that you’re missing the forest for the trees here. In your received(:_) method you’re trying to use information in the message you received to identify the sender. That’s problematic because that data is under the control of the sender, which means that they could pass you anything.
Based on your other post I presume you’re using WebSocket for this. In that case the underlying transport is TCP, which means that you can identify the sender by the connection itself. It seems you have a one-to-one relationship between PeerConnection and NWConnection, and so your receive(_:) method can use self to work out who it’s talking to.
Oh, and I’m concerned about line 9 of your first snippet. Are you actually connecting to every service you discover over Bonjour? That’s generally considered bad form because the act of resolving a service and then connecting to it is quite expensive (especially for dynamic interfaces like peer-to-peer Wi-Fi). It’s better to come up with a design when you only connect to relevant services. For example:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Based on your other post I presume you’re using WebSocket for this. In that case the underlying transport is TCP, which means that you can identify the sender by the connection itself. It seems you have a one-to-one relationship between PeerConnection and NWConnection, and so your receive(_:) method can use self to work out who it’s talking to.
Oh, and I’m concerned about line 9 of your first snippet. Are you actually connecting to every service you discover over Bonjour? That’s generally considered bad form because the act of resolving a service and then connecting to it is quite expensive (especially for dynamic interfaces like peer-to-peer Wi-Fi). It’s better to come up with a design when you only connect to relevant services. For example:
You might present the user with a list of services and have them choose one.
You might remember the user’s previous choice and automatically connect to that.
You might use metadata about the service (in the TXT record) to filter for relevant ones.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"