iOS 局域网通讯 MultipeerConnectivity

问题

最近想做一个小游戏demo,需要这个功能:两台设备使用本地局域网进行游戏。

于是查找到iOS实现局域网的API,最终决定用MultipeerConnectivity实现该功能

解决

根据自己使用的效果,简单进行了封装,可以借鉴
demo地址 MultipeerConnectivity-Demo
原理:一个设备创建一个服务器发送广播,另一个设备创建客服端链接,,,链接上后,双方就可以通讯

实现如下
ConnectBaseViewController.h

#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGINstatic NSString * const ServiceType = @"nearByContent";//这个是当前局域网的标识,需要在info.plist里配置
@interface ConnectBaseViewController : UIViewController
-(instancetype)initWithUserName:(NSString*)name;
-(void)startScan;
-(void)stopScan;
-(void)startPush;
-(void)stopPush;
-(void)sendMsg:(NSString *)msg;
@endNS_ASSUME_NONNULL_END

ConnectBaseViewController.m


#import "ConnectBaseViewController.h"
#import <MultipeerConnectivity/MultipeerConnectivity.h>@interface ConnectBaseViewController ()<MCSessionDelegate,MCNearbyServiceAdvertiserDelegate,MCNearbyServiceBrowserDelegate,NSStreamDelegate>
@property (nonatomic, strong) MCNearbyServiceAdvertiser *advertiser;
@property (nonatomic, strong) MCNearbyServiceBrowser *browser;
@property (nonatomic, strong) MCSession *session;
@property (nonatomic, strong) MCPeerID *peerID;
@property (nonatomic, strong) NSOutputStream *writeStream;
@property (nonatomic, strong) NSInputStream *readStream;
@property (nonatomic, strong) NSMutableArray<MCPeerID *> *dataSource;
@end@implementation ConnectBaseViewController-(instancetype)initWithUserName:(NSString*)name{self = [super init];if(self){[self loadUserInfo:name];}return self;
}- (void)viewDidLoad {[super viewDidLoad];self.dataSource = [NSMutableArray array];// Do any additional setup after loading the view.
}-(void)loadUserInfo:(NSString *)name{//创建用户消息和广播消息池self.peerID = [[MCPeerID alloc] initWithDisplayName:name];self.session = [[MCSession alloc] initWithPeer:self.peerID];//配置消息池代理self.session.delegate = self;
}/***  消息池连通状态改变时调用**  @param session 消息池*  @param peerID  节点信息*  @param state   消息池连通状态*/
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {switch (state) {case MCSessionStateConnecting:NSLog(@"正在链接至:%@",peerID.displayName);break;case MCSessionStateConnected:{NSLog(@"与%@建立链接",peerID.displayName);[self.dataSource addObject:peerID];//链接成功后创建输出流NSError *error;self.writeStream = [self.session startStreamWithName:@"adverting" toPeer:peerID error:&error];if (error) {NSLog(@"输出流创建失败");}//将输出流通道打开,并加入消息循环池[self.writeStream open];[self.writeStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];//展示链接状态dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"已连接");});}break;case MCSessionStateNotConnected:{NSLog(@"与%@无连接",peerID.displayName);[self.dataSource removeObject:peerID];dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"未连接");});}break;default:break;}
}/***  接收到二进制数据时调用**  @param session 信息池*  @param data    二进制数据*  @param peerID  节点信息*/
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {//获取传输数据NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];NSLog(@"接收到%@的消息:%@",peerID.displayName,text);}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{NSLog(@"stream=handleEvent");
}/***  接受到数据流事件请求时调用**  @param session    信息池*  @param stream     输入数据流*  @param streamName 数据流名字*  @param peerID     节点信息*/
- (void) session:(MCSession *)session
didReceiveStream:(NSInputStream *)streamwithName:(NSString *)streamNamefromPeer:(MCPeerID *)peerID {//打开请求的输入流通道,加入消息循环池[stream open];[stream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];//设置代理,以接收数据stream.delegate = self;//持有该输入流self.readStream = stream;NSLog(@"stream=didReceiveStream");
}// Start receiving a resource from remote peer.
- (void)                    session:(MCSession *)sessiondidStartReceivingResourceWithName:(NSString *)resourceNamefromPeer:(MCPeerID *)peerIDwithProgress:(NSProgress *)progress{NSLog(@"3333");
}// Finished receiving a resource from remote peer and saved the content
// in a temporary location - the app is responsible for moving the file
// to a permanent location within its sandbox.
- (void)                    session:(MCSession *)sessiondidFinishReceivingResourceWithName:(NSString *)resourceNamefromPeer:(MCPeerID *)peerIDatURL:(nullable NSURL *)localURLwithError:(nullable NSError *)error{NSLog(@"2222");
}- (MCNearbyServiceAdvertiser *)advertiser {if (_advertiser == nil) {//其中discoveryInfo是展示给Browser端查看的信息可设为nil_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.peerID discoveryInfo:nil serviceType:ServiceType];_advertiser.delegate = self;}return _advertiser;
}- (MCNearbyServiceBrowser *)browser {if (_browser == nil) {_browser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.peerID serviceType:ServiceType];_browser.delegate = self;}return _browser;
}
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary<NSString *,NSString *> *)info {//请求链接到对应的服务节点[browser invitePeer:peerID toSession:self.session withContext:nil timeout:30];NSLog(@"发现%@广播,正在链接...",peerID.displayName);
}
/***  接收到客户端要求链接消息时调用**  @param advertiser        服务端广播*  @param peerID            客户端信息*  @param context           请求内容*  @param invitationHandler 是否接受链接回调函数*/
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession * _Nonnull))invitationHandler {//一般服务端不会拒绝链接所以此处直接链接所有客户端//同意链接并加入广播组消息池NSLog(@"%@申请接入",peerID.displayName);invitationHandler(YES,self.session);
}
// A nearby peer has stopped advertising.
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID{}-(void)startScan{NSLog(@"开始扫描");[self.browser startBrowsingForPeers];
}
-(void)stopScan{NSLog(@"结束扫描");[self.browser stopBrowsingForPeers];[self.writeStream close];[self.readStream close];[self.writeStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[self.readStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[self.session disconnect];
}
-(void)startPush{NSLog(@"开启广播");[self.advertiser startAdvertisingPeer];
}-(void)stopPush{NSLog(@"关闭广播");[self.advertiser stopAdvertisingPeer];//关闭时需要关闭通道[self.writeStream close];[self.readStream close];//从消息循环池中移除[self.writeStream removeFromRunLoop:[NSRunLoop mainRunLoop]  forMode:NSDefaultRunLoopMode];[self.readStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[self.session disconnect];
}-(void)sendMsg:(NSString *)msg{//二进制文件传输方法[self.session sendData:[msg dataUsingEncoding:NSUTF8StringEncoding] toPeers:self.dataSource withMode:MCSessionSendDataReliable error:nil];
}
@end

使用

服务器

ServiceViewController * serviceVc = [[ServiceViewController alloc]initWithUserName:@"service"];[self presentViewController:serviceVc animated:YES completion:^{//[serviceVc startPush];//[serviceVc stopPush];//[serviceVc sendMsg:@"这是一条来自服务器的消息"];}];

客户端

ClientViewController * clientVc = [[ClientViewController alloc]initWithUserName:@"client"];[self presentViewController:clientVc animated:YES completion:^{}];//[clientVc startScan];//[clientVc stopScan];//[clientVc sendMsg:@"这是一条来自客户端的消息"];

遇到的问题及解决方案

问题:报错Server did not publish

[MCNearbyServiceAdvertiser] Server did not publish: errorDict [{NSNetServicesErrorCode = "-72008";NSNetServicesErrorDomain = 10;
}].

解决:在info里配置NSBonjourServices信息
添加_nearByContent._tcp(nearByContent这个string是你工程设置的局域网标识,就是ConnectBaseViewController.h里的ServiceType)

提示:最好在info.plist申明你使用局域网的目的
NSLocalNetworkUsageDescription

联系作者

期待你的点赞和关注!如有疑问,联系作者。

iOS 局域网通讯 MultipeerConnectivity相关推荐

  1. TCP局域网 通讯 的消息发送

    import java.io.*; import java.net.ServerSocket; import java.net.Socket;/*** 初学者TCP局域网 通讯 的消息发送* TCP ...

  2. I-EIM分享一套局域网通讯源码

    通讯员就羡慕得不得了,有一棵棵高达挺拔的白杨, 飞鸽传书 今年内首次载人飞行即将付诸实现,汕头市强大的团队决定将小公园规划为历史风貌保护区,骄傲,我痛下决心,他还有灵活的双手,正在铲着路上的积雪,一定 ...

  3. iOS即时通讯输入框随字数自适应高度

    代码地址如下: http://www.demodashi.com/demo/13210.html 前言 本人最近在研究socket与聊天界面的UI,在写聊天界面UI的时候是模仿微信的界面其中的文字输入 ...

  4. 局域网通讯工具_五大核心开启工业通讯创新之门——西门子工业网络专家计划打造最强行业生态...

        过去的工厂由工具组成,     现在的工厂由设备组成.  过去的工具是割裂的,现在的设备是互联的.收集真实对象信息.传输数据是数字化解决方案的关键要素,工业通讯将成为数字化转型的决定因素. - ...

  5. ios即时通讯客户端开发之-mac上搭建openfire服务器

    CHENYILONG Blog ios即时通讯客户端开发之-mac上搭建openfire服务器 转自:月光的尽头 ios即时通讯客户端开发之-mac上搭建openfire服务器 一.下载并安装open ...

  6. iOS 局域网内搜索硬件设备

    iOS 局域网搜索可以使用两种方式.第一种方式局域网广播方式.此方式一般在3秒内就会相应.第二种方式也是比较笨拙的方式通过ping方式.这种方式一般是在硬件本身并不支持广播功能.此方法弊端:搜索时间长 ...

  7. iOS即时通讯,从入门到“放弃”?

    image 前言 本文会用实例的方式,将iOS各种IM的方案都简单的实现一遍.并且提供一些选型.实现细节以及优化的建议. 注:文中的所有的代码示例,在github中都有demo: iOS即时通讯,从入 ...

  8. iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Connect篇)

    原文 前言: CocoaAsyncSocket是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的.强大的异步套接字库,向上封装出简单易用OC接口.省去了我们面 ...

  9. iOS即时通讯从入门到“放弃”?

    前言 本文会用实例的方式,将iOS各种IM的方案都简单的实现一遍.并且提供一些选型.实现细节以及优化的建议. 注:文中的所有的代码示例,在github中都有demo: iOS即时通讯,从入门到&quo ...

最新文章

  1. @程序员,地表最强的 CSDN 原创博主大赛来了!
  2. Hyper-V 3.0部署PART 14:准备仲裁磁盘
  3. Linux下安装oracle提示INS-20802 Oracle Net Configuration Assistant
  4. 【Python】青少年蓝桥杯_每日一题_4.15_正方形里面套个实心圆形
  5. I NEED A OFFER!
  6. 内存对齐指令详解(posix_memalign)
  7. WinForm窗体之间传值
  8. Asp.Net中WebForm与MVC,Web API模式对比
  9. linux标准I/O——流的相关操作
  10. 拓端tecdat|使用R语言进行多项式回归、非线性回归模型曲线拟合
  11. c语言网吧计费管理小项目,c语言网吧计费系统小项目.doc
  12. 在VM6.5中安装iPC_OSx86_10_5_6_Universal_PPF5_Final
  13. java compar_Java中Comparable和Comparator
  14. 完美池宇峰畅谈创业点滴 男怕入错行
  15. 麦肯锡:释放智能网联汽车数据全生命周期价值​潜力
  16. 简易串口助手通信(齐全) 可实现ASII和十六进制发送指令 并显示
  17. FFmpeg源代码简单分析-其他-日志输出系统(av_log()等)
  18. 直击|咪蒙、才华有限青年注销 旗下公众号清空或停更
  19. The page has expired due to inactivity. Please refresh and try again.
  20. C++ 制作简易音乐播放器

热门文章

  1. redis cluster 4.0.9 之四: redis-trib.rb add-node
  2. Racket编程指南——13 类和对象
  3. 我的大学 --- 郭天祥【1】
  4. STM32通用定时器实现us微秒延时
  5. 思必驰:启发式对话中的知识管理
  6. Globalsign和Symantec SSL证书哪家好
  7. 本题要求实现一个函数,输出n行空心的数字金字塔。
  8. openBoard开源白板项目
  9. python乘法符号手写_利用Python自动生成小学生加减乘除口算考试题卷,不再为手写算术题烦恼!...
  10. 执法部门的“新助手” 扫二维码进群反映问题