为啥多点连接如此缓慢?

Posted

技术标签:

【中文标题】为啥多点连接如此缓慢?【英文标题】:Why is Multipeer Connectivity so slow?为什么多点连接如此缓慢? 【发布时间】:2014-07-15 18:34:35 【问题描述】:

所以,我在主线程中有所有与多点连接相关的代码。我有一个MCSession, MCNearbyServiceAdvertiser, and a MCNearbyServiceBrowser。这些都是用 peerID 创建的,我确保只有一个人发送邀请。

会话已连接。我的问题是,两个客户端大约需要 20-30 秒才能连接。这是不可接受的。客户使用良好的 Wifi 和蓝牙。我希望在 1 秒内完成浏览、邀请处理程序和连接。有谁知道是什么让事情变慢了?

代码与此处提供的完全相同,我还实现了certificateHandler(YES)

@interface SessionController () // Class extension
@property (nonatomic, strong) MCPeerID *peerID;
@property (nonatomic, strong) MCSession *session;
@property (nonatomic, strong) MCNearbyServiceAdvertiser *serviceAdvertiser;
@property (nonatomic, strong) MCNearbyServiceBrowser *serviceBrowser;

// Connected peers are stored in the MCSession
// Manually track connecting and disconnected peers
@property (nonatomic, strong) NSMutableOrderedSet *connectingPeersOrderedSet;
@property (nonatomic, strong) NSMutableOrderedSet *disconnectedPeersOrderedSet;
@end

@implementation SessionController

static NSString * const kMCSessionServiceType = @"mcsessionp2p";

#pragma mark - Initializer

- (instancetype)init

    self = [super init];

    if (self)
    
        _peerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];

        _connectingPeersOrderedSet = [[NSMutableOrderedSet alloc] init];
        _disconnectedPeersOrderedSet = [[NSMutableOrderedSet alloc] init];

        NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];

        // Register for notifications
        [defaultCenter addObserver:self
                          selector:@selector(startServices)
                              name:UIApplicationWillEnterForegroundNotification
                            object:nil];

        [defaultCenter addObserver:self
                          selector:@selector(stopServices)
                              name:UIApplicationDidEnterBackgroundNotification
                            object:nil];

        [self startServices];

        _displayName = self.session.myPeerID.displayName;
    

    return self;


#pragma mark - Memory management

- (void)dealloc

    // Unregister for notifications on deallocation.
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    // Nil out delegates
    _session.delegate = nil;
    _serviceAdvertiser.delegate = nil;
    _serviceBrowser.delegate = nil;


#pragma mark - Override property accessors

- (NSArray *)connectedPeers

    return self.session.connectedPeers;


- (NSArray *)connectingPeers

    return [self.connectingPeersOrderedSet array];


- (NSArray *)disconnectedPeers

    return [self.disconnectedPeersOrderedSet array];


#pragma mark - Private methods

- (void)setupSession

    // Create the session that peers will be invited/join into.
    _session = [[MCSession alloc] initWithPeer:self.peerID];
    self.session.delegate = self;

    // Create the service advertiser
    _serviceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.peerID
                                                           discoveryInfo:nil
                                                             serviceType:kMCSessionServiceType];
    self.serviceAdvertiser.delegate = self;

    // Create the service browser
    _serviceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.peerID
                                                       serviceType:kMCSessionServiceType];
    self.serviceBrowser.delegate = self;


- (void)teardownSession

    [self.session disconnect];
    [self.connectingPeersOrderedSet removeAllObjects];
    [self.disconnectedPeersOrderedSet removeAllObjects];


- (void)startServices

    [self setupSession];
    [self.serviceAdvertiser startAdvertisingPeer];
    [self.serviceBrowser startBrowsingForPeers];


- (void)stopServices

    [self.serviceBrowser stopBrowsingForPeers];
    [self.serviceAdvertiser stopAdvertisingPeer];
    [self teardownSession];


- (void)updateDelegate
    
    [self.delegate sessionDidChangeState];


- (NSString *)stringForPeerConnectionState:(MCSessionState)state

    switch (state) 
        case MCSessionStateConnected:
            return @"Connected";

        case MCSessionStateConnecting:
            return @"Connecting";

        case MCSessionStateNotConnected:
            return @"Not Connected";
    


#pragma mark - MCSessionDelegate protocol conformance

- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state

    NSLog(@"Peer [%@] changed state to %@", peerID.displayName, [self stringForPeerConnectionState:state]);

    switch (state)
    
        case MCSessionStateConnecting:
        
            [self.connectingPeersOrderedSet addObject:peerID];
            [self.disconnectedPeersOrderedSet removeObject:peerID];
            break;
        

        case MCSessionStateConnected:
        
            [self.connectingPeersOrderedSet removeObject:peerID];
            [self.disconnectedPeersOrderedSet removeObject:peerID];
            break;
        

        case MCSessionStateNotConnected:
        
            [self.connectingPeersOrderedSet removeObject:peerID];
            [self.disconnectedPeersOrderedSet addObject:peerID];
            break;
        
    

    [self updateDelegate];


- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID

    // Decode the incoming data to a UTF8 encoded string
    NSString *receivedMessage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(@"didReceiveData %@ from %@", receivedMessage, peerID.displayName);


- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress

    NSLog(@"didStartReceivingResourceWithName [%@] from %@ with progress [%@]", resourceName, peerID.displayName, progress);


- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error

    NSLog(@"didFinishReceivingResourceWithName [%@] from %@", resourceName, peerID.displayName);

    // If error is not nil something went wrong
    if (error)
    
        NSLog(@"Error [%@] receiving resource from %@ ", [error localizedDescription], peerID.displayName);
    
    else
    
        // No error so this is a completed transfer.  The resources is located in a temporary location and should be copied to a permenant location immediately.
        // Write to documents directory
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *copyPath = [NSString stringWithFormat:@"%@/%@", [paths firstObject], resourceName];
        if (![[NSFileManager defaultManager] copyItemAtPath:[localURL path] toPath:copyPath error:nil])
        
            NSLog(@"Error copying resource to documents directory");
        
        else
        
            // Get a URL for the path we just copied the resource to
            NSURL *url = [NSURL fileURLWithPath:copyPath];
            NSLog(@"url = %@", url);
        
    


// Streaming API not utilized in this sample code
- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID

    NSLog(@"didReceiveStream %@ from %@", streamName, peerID.displayName);


#pragma mark - MCNearbyServiceBrowserDelegate protocol conformance

// Found a nearby advertising peer
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info

    NSString *remotePeerName = peerID.displayName;

    NSLog(@"Browser found %@", remotePeerName);

    MCPeerID *myPeerID = self.session.myPeerID;

    BOOL shouldInvite = ([myPeerID.displayName compare:remotePeerName] == NSOrderedDescending);

    if (shouldInvite)
    
        NSLog(@"Inviting %@", remotePeerName);
        [browser invitePeer:peerID toSession:self.session withContext:nil timeout:30.0];
    
    else
    
        NSLog(@"Not inviting %@", remotePeerName);
    

    [self updateDelegate];


- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID

    NSLog(@"lostPeer %@", peerID.displayName);

    [self.connectingPeersOrderedSet removeObject:peerID];
    [self.disconnectedPeersOrderedSet addObject:peerID];

    [self updateDelegate];


- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error

    NSLog(@"didNotStartBrowsingForPeers: %@", error);


#pragma mark - MCNearbyServiceAdvertiserDelegate protocol conformance

- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler

    NSLog(@"didReceiveInvitationFromPeer %@", peerID.displayName);

    invitationHandler(YES, self.session);

    [self.connectingPeersOrderedSet addObject:peerID];
    [self.disconnectedPeersOrderedSet removeObject:peerID];

    [self updateDelegate];


- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error

    NSLog(@"didNotStartAdvertisingForPeers: %@", error);


@end

【问题讨论】:

客户端是否在同一个 WiFi 上?我注意到如果它们是不同的网络,它们需要很长时间才能连接。 他们都在wifi,蓝牙上。我也只尝试过一种媒介。一样的 但是当他们使用 WiFi 时,是同一个网络吗?当我将一个放在 5GHz 上,另一个放在 2.4Ghz 上时,我可以重现 20 秒的延迟。 延迟到底发生在哪里?是在他们见面之前,在邀请接受阶段,还是在连接之后但在发送数据之前? 它是发现、邀请、接受阶段。 【参考方案1】:

我已经注意到了这个问题,但我似乎无法弄清楚这里发生了什么,在调试时似乎在触发状态更改通知之间存在巨大延迟,可能与视图本身有关.

更新:好吧,我想我在阅读了一些内容后发现了问题,现在响应几乎是即时的,在视图中的通知中,我已将进程推回主线程,如下所示:

-(void)peerDidChangeStateWithNotification:(NSNotification *)notification

        MCPeerID *peerID = [[notification userInfo] objectForKey:@"peerID"];
        NSString *peerDisplayName = peerID.displayName;
        MCSessionState state = [[[notification userInfo] objectForKey:@"state"] intValue];

        if (state != MCSessionStateConnecting) 

            if (state == MCSessionStateConnected) 
                // add the user
                [arrConnectedDevices addObject:peerDisplayName];
            
            else if (state == MCSessionStateNotConnected)

                // do we have connections
                if ([arrConnectedDevices count] > 0) 
                    int indexOfPeer = [arrConnectedDevices indexOfObject:peerDisplayName];
                    [arrConnectedDevices removeObjectAtIndex:indexOfPeer];
                
            
        

    // push to main queue for speedy response
    dispatch_async(dispatch_get_main_queue(), ^(void) 

        [collView reloadData];

        BOOL peersExist = ([[appDelegate.mcManager.session connectedPeers] count] == 0);
        NSLog(@"PEER COUNT IS %lu",(unsigned long)[[appDelegate.mcManager.session connectedPeers] count]);
        [disconnectButton setEnabled:!peersExist];

        if ([disconnectButton isEnabled]) 
            [disconnectButton setBackgroundColor:[UIColor colorWithRed:(51/255.0) green:(202/255.0) blue:(168/255.0) alpha:1.0]];
        
        else
            [disconnectButton setBackgroundColor:[UIColor colorWithRed:(107/255.0) green:(107/255.0) blue:(107/255.0) alpha:1.0]];
        

    );


希望这对遇到问题的人有所帮助。

【讨论】:

嗨,这很简单。任何需要快速更改 ui 的东西都需要在主线程上调度。我的问题涉及一个不同的问题。无论如何,我找到了解决方案。我很快就会在这里发帖。 @Legolas 我也会对您的解决方案感兴趣。 @Legolas 对此有何意见? @Legolas 您是否找到了加快速度的解决方案?谢谢 @Legolas,你的姓是 Fermat 吗? ;)

以上是关于为啥多点连接如此缓慢?的主要内容,如果未能解决你的问题,请参考以下文章

如何找出为啥 wordpress 博客加载如此缓慢

为啥 Rails 渲染视图如此缓慢?

为啥从 Redshift 读取到 Spark 如此缓慢?

为啥在索引 Parquet 文件上计算形状如此缓慢?

为啥 Composer 中的依赖项更新如此缓慢?

为啥我的 python DataFrame 执行如此缓慢