Reconnecting to Disconnected Peers

How to correctly tear down multipeer connectivity session?

I reset the PeerID and Session on the Browser side whenever I get disconnected and want to reconnect:

    // reset my PeerID.  Sometimes when trying to reconnect to the same Advertiser with the same PeerID nothing happens
mcPeerID = [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name];
mcSession = [[MCSession alloc] initWithPeer:mcPeerID];
mcSession.delegate = self;

mcNearbyServiceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:mcPeerID serviceType:kMCServiceType];
mcNearbyServiceBrowser.delegate = self;
[mcNearbyServiceBrowser startBrowsingForPeers];

On the Advertiser side, the connectedPeers count is decremented correctly on the disconnect, so reconnecting as a * different * browser doesn't seem to have a negative impact (maybe this isn't proper coding, but it works). With a new PeerID I can reconnect without issue. I reset the Session at the same time to keep them in sync (ie, I don't want a Session laying about that was init'd from the old PeerID).

MultipeerConnectivity - MCNearbyServiceBrowser constantly finding disconnected peers

I found a really helpful answer at the Apple Dev Forums.

Here the link.

Basically what resolved this issue was recycling the MCPeerID. When the object is created I serialize it and stored in NSUserDefaults. And anytime I need it back, like when I tear down the service and start it again I go to the stored object and used it instead of creating a new one.

You can find the next example code in the attached link above:

- (MCPeerID *)peerID {
if (!_peerID) {
_peerID = [MyClassName getRecycledPeerID];
}
return _peerID;
}

+ (MCPeerID *)getRecycledPeerID
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

// if peer id exists, use that; else create one
if ([defaults objectForKey:kRECYCLED_PEER_ID]) {
NSData *peerIDData = [defaults dataForKey:kRECYCLED_PEER_ID];
return [NSKeyedUnarchiver unarchiveObjectWithData:peerIDData];
}
else {
return [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name];
}
}

Reconnecting serveral peerConnections after page reload

Remove the TempDescriptions global variable, and pass the sdp to getStream(offer.sdp) directly.

Otherwise, you've socket.on('ask', function(offer){ called 4 times, overwriting TempDescriptions. Then 3+ seconds later your 4 setTimeouts roll around, all accessing the final value of TempDescriptions only.

That's probably why only one RTCPeerConnection re-connects.

In general, using time delay to separate connections seems like a bad idea, as it slows down re-connection. Instead, why not send an id? E.g.

socket.emit('ask', {id: connectionNumber++,
sdp: JSON.stringify(pc.localDescription),
user: loggedUserID,
fromSocket: ownSocket});

Update: Stop adding global variables to window

Whenever you assign to an undeclared variable like this:

connection = getPeerConnection();

...it creates a global on window, e.g. window.connection, and you have the same problem. You have 4 connection, but you're storing them in one variable.

Type "use strict"; at the head of your source file to catch this:

ReferenceError: assignment to undeclared variable connection

Scoping: The general problem

You're dealing with 4 connections here, but you lack an approach for scoping each instance.

Most other languages would tell you to create a class and make object instances, and put everything including connection on this. That's one good approach. In JS you can use closures instead. But at minimum you still need 4 variables holding the 4 connections, or an array of connections. Then you look up—e.g. from the id I mentioned—which connection to deal with.

Also, your try/catches aren't going to catch asynchronous errors. Instead of defining all these callbacks, I strongly recommend using promises, or even async/await when dealing with the highly asynchronous WebRTC API. This makes scoping trivial. E.g.

const connections = [];

socket.on('ask', async ({user, id, sdp, fromSocket}) => {
try {
if (user != loggedUserID) return;
if (!connections[id]) {
connections[id] = getPeerConnection();
registerIceCandidate(connections[id]);
}
const connection = connections[id];
await connection.setRemoteDescription(JSON.parse(sdp));
await connection.setLocalDescription(await connection.createAnswer());
socket.emit('response', {sdp,
user: loggedUserID,
fromSocket: ownSocket,
toSocket: fromSocket});
} catch (err) {
console.log(err);
}
};

This way the error handling is solid.



Related Topics



Leave a reply



Submit