📜 ⬆️ ⬇️

Fear and Loathing in Multipeer Connectivity


Author: Roman Ivchenko, iOS developer DataArt.

Introduction

Surely, anyone who has ever searched for a ready solution for exchanging messages, files, streams between iOS devices without using the server part, heard about the Multipeer Connectivity framework released in iOS 7.

In general, it is one of the most innovative frameworks released in the 7th version of the system. It had to replace the slightly outdated CoreBluetooth.
')
In order to learn all the power and strength of Multipeer Connectivity, we tried to run it in our R & D project , whose task is quite simple - sharing presentations and synchronizing slides between listeners' devices and speaker's device at conferences, in classrooms, etc.

Short review

To accomplish our task, the framework, at first glance, very well fit into the architecture of the application. Conventionally, we have only two types of users - a speaker and a listener. Multipeer Connectivity just provides the necessary classes for the implementation of the functionality of each type of user.

The article does not claim to provide full coverage of all the subtleties of the framework, but tells more about its problems and reliability. All technical details can be found in the Apple documentation .

Speaker aka Advertiser



The mechanism is simple. There is a session that is initialized by the security and encryption settings, as well as by the object of the class MCPeerID, the essence of which is quite minimalist, since this class has only one property - displayName. It, in fact, can be considered the name of the ID session. In order for our session to be seen by other users, the framework provides us with the MCAdvertiserAssistant class, a beacon that informs everyone about this session and stores information about it.

As soon as the listener wants to connect to the session, MCAdvertiserAssistant will automatically show a notification about this, with the options "allow / reject connection". As soon as the user allows the advertizer to connect to the listener, he enters the session and gets the opportunity to communicate with the advertizer.

Listener aka Browser

In the case of the listener, it is still easier - the developer writes the listener side code with minimal effort using the MCBrowserViewController framework controller, which completely takes over the entire search and connection logic to the Advertiser or implements its own controller. In our application, the second approach was used, since the first, as it turned out, in terms of stability and quality of work, corresponds quite well to the title of the article. But more on that later.



For a browser-based device, the MCNearbyServiceBrowser class is provided, something like a radar that is looking for advertisers within a specific service name.

Communication between MCNearbyServiceBrowser and the controller class is implemented through the MCNearbyServiceBrowserDelegate protocol, where the logic of the delegate methods implemented implies the display of current active sessions of advertizers, changing their states.

It is important to note that a session is created on the browser side, to which MCNearbyServiceBrowser invites an Advertiser. As soon as the advertiser accepts the invitation from the listener, the devices can be considered connected to each other.

The message sending interface is also very transparent and simple, the advertiser and the browser send messages to each other in NSDat format, and the message receiving processing, connection status between devices, file transfer progress can be traced by implementing the MCSessionDelegate protocol methods.

First touch

In theory and even, at first glance, in practice, everything looks very convenient in terms of the implementation of the architecture - the developer gets access only to the most necessary, saving himself from the complex logic of working with wifi / bluetooth networks.

As usual, before starting to integrate the framework into the project, everyone wants to see how it works in Apple’s official demo project. MultipeerGroupChat basically shows what's what, and it works quite stably with a starter pack-set - a simulator and an iPhone / iPod / iPad. Developers who have the opportunity to look at the demo application, having more than two devices, can immediately feel: that something in this framework is amiss.

Ghost sessions

The first bug of the framework that immediately catches the eye, even if you have the simulator and the device with you to test it - this is the problem of ghost sessions.

Imagine the situation. Alice is a speaker (Advertiser), Bob is a listener (browser). Alice starts the presentation session and Bob connects to it, they exchange a couple of messages, everything is fine. Alice finishes the presentation, and her session ends. The first is that the probability that Bob will receive a notification that there is no longer an Advertiser, is a little more likely that he will not.

For a browser-device, a non-existent Advertiser session may exist indefinitely. When you try to connect to this session, nothing can happen, or after a while the connection attempt goes into the failed state. The problem takes on serious proportions if an Advertiser has created its sessions several times. In this case, the browser in the MCBrowserViewController native controller may display several identical Advertisers of the iPhone Simulator, when you work with only one device and one simulator, and you have no idea which advertiser from the list is active. An example of a bug in the Apple Demo application:



By the way, this problem is not solved by either restarting the application or reinstalling it. Older sessions may still haunt you. It only helps on and off the airplane mode.

Workaround:

When we have only one Advertiser, each new session of this Advertiser must have discoveryInfo. This parameter is in the Dictionary <String, String> format, which is set at the start of the session of the advertizer and contains additional information about the session. By adding the session creation time as a unique identifier for each session, on the browser side you can track which sessions to show and which not.



If you do everything right, the list of active sessions for each advertizer device will be the most recent session.



The problem of the maximum number of devices in the session

Strange, but initially the framework has a limitation of seven connectable devices in one session. Goals, for example, our task, clearly go against this restriction from Apple engineers. When using our application, 30 - 40 devices can participate at the same time, and it is a pity that the framework does not initially have a solution for such a case.

Workaround:

Despite the limitation of the framework on the number of devices in a session, it does not have a limit on the number of sessions. In order, for example, to have one edvertizer had the opportunity to exchange data with 40 browsers, you need to implement a solution that can support work with six sessions. But here, again, the problem: the browser-device will see several sessions from the same Advertiser, and you need to make sure that the browser sees the most recent session with free places to connect. As an option, the same discoveryInfo parameter comes to the rescue, in which you can contain, say the session index.

The mechanism of management of additional sessions on the advertiser:




On the browser side, you must filter all sessions by index and display the session with the maximum index value. If in the previous sessions there is suddenly a free space, we will not be able to inform the browser about this, which wants to connect to the Advertiser, since the MCAdvertiserAssistant class discoveryInfo property is readonly.

Reconnection

I consider this framework headache to be the most costly for crushing with crutches. I think it is very strange for Apple to release the framework without a ready-made reconnect mechanism. The usual case - the browser device went into a slip mod, for 15-20 seconds it can still receive messages from the advertiser, but then the framework informs us that connection lost ...

Workaround:

It would seem that in order to connect again to the session of the converter, you just need to always store a pointer to the object of this session, and in which case re-send the invitation to the advertiser using this session. In practice, this obvious approach does not work. In our project, only hard reset of the browser-session and everything that was connected with it, and the process emulation, as if the user had updated the list of available advertizers and connected to the one from which he disconnected after leaving the background, helped.



The solution is very rough, and I am sure that for some projects and tasks it is absolutely inappropriate, but it is apparently impossible to do it differently. Proof , Problem 4.

General work instability, sudden connection lost


If the previous bugs and limitations could somehow be solved, everything depends on the Apple engineers. According to my observations, out of 10 attempts to test the simplest cases for our application, approximately 7-8 completed more or less successfully. Simple cases - about 10 - 15% of the maximum possible load of the application (Advertiser and 20 - 30 browsers). In the remaining feil cases, the following occurred:



The first problem mentioned above can be solved by implementing a health check mechanism. At certain intervals, the browser sends a lightweight ping message, which is waiting for a response. If the answer does not come within a few seconds, we can assume that the connection with the Advertiser is lost. In our project, this mechanism was implemented as follows:




Increasing the number of connected devices proportionately reduces the stability of the entire system.

This problem is the main one in the article. If in the case of two or three devices the stability of work is more or less normal, with seven to nine, not to mention more quantity, then stability begins to tend to zero. The framework just doesn't work. I’ll make a reservation right away: this is about a connection such as an “Advertiser and many browsers”. Perhaps there are some other more stable configurations, but this one is the easiest to implement, and when using the framework, you want it to work equally stable for all configurations.

The solution for this problem during the work on the project was not found, and was not searched further, since it was more and more like sticking a riddled inflatable boat in the middle of the ocean.

Fear, hate and conclusions

While working on this project and exploring this framework, I still hoped to the end that I was doing something wrong and that Apple couldn’t screw up so much. But after a couple of hours of research, I found many similar complaints from the developers and a good article devoted to the same problems.

Many messages are two years old, and you can first think that all of this is the framework's childhood diseases, and in iOS 7 it was implemented in the alpha version, but now it's 2016, the latest version of iOS is 9.2, and the problems remain the same .

MultipeerConnectivity is not highly recommended for use with more than three devices. For two or three devices, the forecast is favorable, but there will still be a lot of problems: in order to achieve what, in theory, should be out of the box, you need to spend twice as much time.
The article will end well with a couple of comments on the topic on devforums.apple.com/message/956192#956192 and a link to our application on GitHub github.com/DataArt/SmartSlides .





Confirmation of the problem :
www.ymc.ch/en/multipeer-connectivity-a-bag-of-hurt
stackoverflow.com/questions/28418965/multipeer-connectivity-vs-real-time-matches
devforums.apple.com/message/956192#956192
devforums.apple.com/message/982861#982861

Source: https://habr.com/ru/post/275627/


All Articles