Multipeer Connectivity 近场多点通信(二)

1,490 阅读4分钟

往期文章

寻找IOS相册中相似图片
NSNotification与类对象,实例对象
CocoaPods私有源搭建
雷达扩散效果搜索设备
Multipeer Connectivity 近场多点通信(一)

本节内容

在上一节的内容当中,我们简单介绍了Multipeer Connectivity这个框架中各个模块的功能以及建立一个简单的通信连接,在本节内容当中主要讲解发送数据与接受数据两个功能的简单使用

数据的发送与接收

根据上一节的介绍,我们了解到在近场服务通信框架中。MCPeerID代表在整个通信服务中设备的唯一ID,MCSession用于保证端点与端点之间的数据传输,以及会话保持等功能。所以根据这个两个模块,我们就可以组合出一个点对点的数据发送功能。

连接状态

发送数据之前,我们得保证连接是稳定的,并且可靠的。

通过实现MCSession委托的协议,我们就能监测到连接的当前状态。

- (void)session:(nonnull MCSession *)session peer:(nonnull MCPeerID *)peerID didChangeState:(MCSessionState)state {
    if (state == MCSessionStateConnected) {
        NSLog(@"会话已经建立");
    }

    else if (state == MCSessionStateConnecting){
        NSLog(@"会话正在建立");
    }
    else{
        NSLog(@"会话未能建立");
    }

}

sendData

在确定连接已经建立的情况下,接下来我们就可以开始发送数据了

- (void) sendData:(NSData*) data {

    //先判断是否有连接的设备
    if (self.session.connectedPeers.count <= 0) {
        //无连接设备,退出链接
        return;
    }

    //此处是点对点连接,也就是一对一连接,所以这里只需要去连接数组中的一个对等点

    MCPeerID* sendPeerID = self.session.connectedPeers.firstObject;

    //开始发送数据
    NSError* sendError = nil;
    [self.session sendData:data toPeers:@[sendPeerID] withMode:MCSessionSendDataReliable error:&sendError];

    if (!sendError) {
        NSLog(@"发送成功");
    }

    else {
        NSLog(@"发送失败 %@",sendError);
    }
}

在上面代码中,因为Multipeer Connectrivity直接一对多的传输模式,也就是说一个对等点可以与多个对等点建立连接,并且同时向多个对等点发送数据。当前节点已经建立连接的对等点,存放在MCSession的connectedPeers中。

@property (readonly, NS_NONATOMIC_IOSONLY) NSArray<MCPeerID *> *connectedPeers;

因为这里我们采用的点对点连接,没有考虑一对多的情况,所以我们只需要取数组的第一个MCPeerID就行了。 接下来就是调用Session的SendData方法发送数据,这里发送数据需要设置数据的发送模式,类似于TCP还是UDP。

typedef NS_ENUM (NSInteger, MCSessionSendDataMode) {

    MCSessionSendDataReliable,      // 以可靠模式发送,

    MCSessionSendDataUnreliable     // 立即发送,无需排队,不保证数据一定到达
}

发送数据之后,接收方通过实现MCSession的协议委托方法didReceiveData,即可完成数据接收

- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
    NSLog(@"接收到 %@ 的消息 %@",peerID.displayName,data);
}

sendResourceUrl

同时MCSession也支持以URL的形式发送数据,可以用于处理大型资源文件(比如视频,图片等等)。并且可以检测资源的发送进度。


- (void) sendResourceUrl:(NSURL*) url resourceName:(NSString*) name progress:(AMSProgressBlock) progress complete:(AMSCompleteBlock) complete {

    NSProgress* progressObject = [self.session sendResourceAtURL:url withName:name toPeer:self.session.connectedPeers.firstObject withCompletionHandler:^(NSError * _Nullable error) {

        complete(name,error);

        //传输完成之后删除tmp目录中的资源

        [AMSanBoxManager removeItemAtURL:url];

    }];

    [self observeProgress:progressObject block:progress];
}

在上面代码中,我们通过调用MCSession的sendResourceUrl方法将本机的资源文件的URL,文件名称,以及接收者ID传入。

此时sendResourceUrl会给我们返回一个NSProgress,用来获取进度,或者暂停任务。

这里我们通过KVO来监听这个资源传输任务的进度

- (void) observeProgress:(NSProgress*) progress block:(AMSProgressBlock) block {
    self.block = block;
    [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([object isKindOfClass:[NSProgress class]]) {

        NSProgress* progress = (NSProgress*) object;

        NSLog(@"进度= %f",progress.fractionCompleted);

        NSLog(@"已传输任务 %lld / 当前总任务 %lld ",progress.completedUnitCount,progress.totalUnitCount);

        if (self.block) {

            self.block(progress.fractionCompleted);
        }
    }
}

MCSession的sendUrlResource会根据我们的提供的图片URL,将图片URL拷贝到沙盒的temp目录下面。然后再进行发送。

相册中图片的URL

file:///private/var/mobile/Containers/Data/Application/7FB4397B-E71A-41C8-B700-9046EABF40B4/tmp/D3CC912E-9364-4EC9-B3B9-CB5EAC6F088A.jpeg

在资源成功发送之后,接收方需要实现MCSession的协议委托的方法。

首先是didStartReceivingResourceWithName方法。用于处理资源接收过程中的进度,以及资源开始接收时的一些设置。

- (void)                    session:(MCSession *)session

  didStartReceivingResourceWithName:(NSString *)resourceName

                           fromPeer:(MCPeerID *)peerID

                       withProgress:(NSProgress *)progress {

    NSLog(@"---------------开始接收---------------");

    NSLog(@"项目名称 %@",resourceName);


    [self observeProgress:progress block:^(CGFloat progress) {

        NSLog(@"接收进度 %f", progress);
    }];
}

在资源接收完毕之后,MCSessionDelegate协议会调用didFinishReceivingResourceWithName,来告诉受托方,资源已经接收完毕。

- (void)                    session:(MCSession *)session

 didFinishReceivingResourceWithName:(NSString *)resourceName

                           fromPeer:(MCPeerID *)peerID

                              atURL:(nullable NSURL *)localURL

                          withError:(nullable NSError *)error {

    NSLog(@"---------------接收完成---------------");

    NSLog(@"本地存储路径 %@",localURL.absoluteString);

    NSLog(@"资源名称 %@",resourceName);


    NSURL* restoreURL = [AMSanBoxManager URLWithSavedName:localURL name:resourceName];

   [AMPhotoKitManager testSaveAssetsWithFilePath:restoreURL];

}

同样,在资源接收完毕之后。MCSession会将这个接收到的资源存在沙盒目录的temp目录下面,这时候需要我们将这个temp目录中接收到的资源,转移到Documents或者相册等其他永久性存储的地方。

image.png

我们将系统随机命名的资源名称,修改为我们传过来的资源名称,然后将修改后的资源,移动到Documents目录中,或者相册中,完成之后再将随机命名的文件删除掉。

从而完成了我们的资源发送功能。