简易描述
MultipeerConnectivity框架可用来进行近场通讯,它兼容了WiFi和蓝牙的功能,只要两台手机同时开启了WiFi或者同时开启了蓝牙,就能进行通讯及数据传输。MultipeerConnectivity(以下简称MC)的使用也很简单,苹果已经封装的很好了。
MC框架的使用可以用创建房间和加入房间作比喻,A端打开房间,允许外部设备扫描,B端打开扫描器,扫描开放的房间,扫描到后B端发送指令申请加入,A端接收到指令同意加入请求,两端通过监测连接状态判断是否成功连接。
使用方法
导入MC框架
#import
MC对象
@property(nonatomic,strong) MCPeerID *peerID; //用作房间的创建,peerID.displayName为房间名
@property(nonatomic,strong) MCSession *session; //管理对象
@property(nonatomic,strong) MCNearbyServiceAdvertiser *serviceAdvertiser; //创建房间相关业务
@property(nonatomic,strong) MCNearbyServiceBrowser *serviceBrowser; //扫描房间相关业务
懒加载
#pragma mark - 懒加载
- (MCPeerID *)peerID {
if (!_peerID) {
_peerID = [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name];
}
return _peerID;
}
- (MCSession *)session {
if (!_session) {
_session = [[MCSession alloc] initWithPeer:self.peerID];
_session.delegate = self;
}
return _session;
}
- (MCNearbyServiceAdvertiser *)serviceAdvertiser {
if (!_serviceAdvertiser) {
_serviceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.peerID discoveryInfo:nil serviceType:serviceTypeString];
_serviceAdvertiser.delegate = self;
}
return _serviceAdvertiser;
}
- (MCNearbyServiceBrowser *)serviceBrowser {
if (!_serviceBrowser) {
_serviceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.peerID serviceType:serviceTypeString];
_serviceBrowser.delegate = self;
}
return _serviceBrowser;
}
别忘了遵循代理
//打开房间
[self.serviceAdvertiser startAdvertisingPeer];
//扫描房间
[self.serviceBrowser startBrowsingForPeers];
//关闭房间
[_serviceAdvertiser stopAdvertisingPeer];
//停止扫描
[_serviceBrowser stopBrowsingForPeers];
代理回调方法
#pragma mark - MCSessionDelegate
//监测连接状态
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
switch (state) {
case MCSessionStateConnecting:
CCLog(@"正在连接...");
break;
case MCSessionStateConnected:
CCLog(@"连接成功!");
[self stop];
break;
case MCSessionStateNotConnected:
default:
CCLog(@"连接失败~");
break;
}
}
//小数据的接收
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
CCLog(@"小数据接收");
}
//对流stream的接收
- (void)session:(MCSession *)session
didReceiveStream:(NSInputStream *)stream
withName:(NSString *)streamName
fromPeer:(MCPeerID *)peerID {
CCLog(@"stream 接收");
}
//开始从远程端口接收文件
- (void)session:(MCSession *)session
didStartReceivingResourceWithName:(NSString *)resourceName
fromPeer:(MCPeerID *)peerID
withProgress:(NSProgress *)progress {
CCLog(@"文件 接收...");
//_progress为 NSProgress,用来监听文件传输进度,具体KVO方法在下面有
if (_progress) {
[_progress removeObserver:self forKeyPath:@"completedUnitCount"];
_progress = nil;
}
_progress = progress;
//KVO观察
[_progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];
}
//接收文件完成
- (void)session:(MCSession *)session
didFinishReceivingResourceWithName:(NSString *)resourceName
fromPeer:(MCPeerID *)peerID
atURL:(NSURL *)localURL
withError:(nullable NSError *)error {
CCLog(@"文件接收完成!");
}
#pragma mark - KVO
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if (object == _progress) {
int64_t numberCom = _progress.completedUnitCount;
int64_t numberTotal = _progress.totalUnitCount;
CGFloat progress = (float)numberCom / (float)numberTotal;
}
}
#pragma mark - MCNearbyServiceAdvertiserDelegate
//收到加入请求
- (void) advertiser:(MCNearbyServiceAdvertiser *)advertiser
didReceiveInvitationFromPeer:(MCPeerID *)peerID
withContext:(nullable NSData *)context
invitationHandler:(void (^)(BOOL accept, MCSession * __nullable session))invitationHandler {
CCLog(@"收到加入请求");
invitationHandler(YES,self.session); //NO为不同意加入请求
}
//连接错误时
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error {
CCLog(@"didNotStartAdvertisingPeer error = %@",error);
}
#pragma mark - MCNearbyServiceBrowserDelegate
//搜索到房间
- (void)browser:(MCNearbyServiceBrowser *)browser
foundPeer:(MCPeerID *)peerID
withDiscoveryInfo:(nullable NSDictionary *)info {
CCLog(@"搜索到的设备名 %@",peerID.displayName);
if (![_peerIDs containsObject:peerID]) {
[_peerIDs addObject:peerID];
}
}
//丢失房间信息
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID {
CCLog(@"丢失房间信息 %@",peerID);
}
//连接错误
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error {
CCLog(@"连接错误");
}
- (void)retset {
//重置
if (_session) {
[_session disconnect];
_session = nil;
}
if (_serviceAdvertiser) {
[_serviceAdvertiser stopAdvertisingPeer];
_serviceAdvertiser = nil;
}
if (_serviceBrowser) {
[_serviceBrowser stopBrowsingForPeers];
_serviceBrowser = nil;
}
_peerIDs = @[].mutableCopy;
}
具体的使用可以下载demo进行参考,由上面的代码可以看出使用还是比较简单的,在用途上可以做设备间的信息传输,可以实现一个短距离的即时通讯(好像没什么卵用),当然具体的方向就看需求了。
备注:文中的CCLog不用在意,替换成NSLog就行;文中还有stream流传输没有写,这里贴部分代码参考一下吧。
//发送stream数据
- (void)sendStreamWith:(NSString *)path {
self.byteIndex = 0; //NSInteger
_filePath = path;
self.streamData = [NSMutableData dataWithContentsOfFile:_filePath];
NSLog(@"self.streamData.length = %ld",self.streamData.length);
NSError *error;
self.outputStream = [self.session startStreamWithName:@"super stream" toPeer:[self.session.connectedPeers firstObject] error:&error];
self.outputStream.delegate = self;
[self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
if(error || !self.outputStream) {
NSLog(@"%@", error);
}
else{
[self.outputStream open];
}
}
#pragma mark - NSStreamDelegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventOpenCompleted:{
NSLog(@"数据流开始,初始化数据");
self.byteIndex = 0;
self.streamData = [[NSMutableData alloc]init];
}
break;
case NSStreamEventHasBytesAvailable:{
//有数据可用
NSInputStream *input = (NSInputStream *)aStream;
uint8_t buffer[1024];
NSInteger length = [input read:buffer maxLength:1024];
NSLog(@"有数据可用: %ld", length);
[self.streamData appendBytes:(const void *)buffer length:(NSUInteger)length]; //一点一点的进行传输
}
break;
case NSStreamEventHasSpaceAvailable:{
//有空间可以存放
CCLog(@"有空间可以存放");
self.streamData = [NSMutableData dataWithContentsOfFile:_filePath];
NSOutputStream *output = (NSOutputStream *)aStream;
NSUInteger len = ((self.streamData.length - self.byteIndex >= 1024) ? 1024 : (self.streamData.length-self.byteIndex));
NSData *data1 = [self.streamData subdataWithRange:NSMakeRange(self.byteIndex, len)];
[output write:data1.bytes maxLength:len];
self.byteIndex += len;
dispatch_async(dispatch_get_main_queue(), ^{
//进度: (float)self.byteIndex / (float)self.streamData.length;
});
}
break;
case NSStreamEventEndEncountered:{
//结束
CCLog(@"结束");
[aStream close];
[aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
if([aStream isKindOfClass:[NSInputStream class]]){
//将self.streamData存入本地即可保存
}
self.byteIndex = 0;
}
break;
case NSStreamEventErrorOccurred:{
//发生错误
NSLog(@"error");
}
break;
default:
break;
}
}
github链接:
https://github.com/602530268/MC_Demo2