+一般在进行Socket编程或者网络访问的时候,首先需要确认对方网络服务已经开启,且需要知道对方的域名或地址以及端口,然后才可以进行进一步操作。在互联网上好点,网络服务方一般常年开启,且一般IP地址是固定的,另由于DNS服务的存在,只要记住对方的域名便可以。但是在局域网,设备不一定连在上面,即使连上了,服务也不一定开了,每当设备连接到局域网的时候,IP地址一般都是动态分配的,所以情况变的复杂。Bonjour的存在便是苹果为了解决局域网设备间连接麻烦的问题。 |
||
| 5 |
+直白的说Bonjour就是是一种协议,使得局域网中的计算机可以方便的发布服务,发现服务和连接服务,达到零配置([Zeroconf][link-zeroconf])的目的。 |
|
| 6 |
+[link-zeroconf]:http://zeroconf.org |
|
| 7 |
+ |
|
| 8 |
+Zeroconf Working Group指出要实现零配置网络服务的3个要求: |
|
| 9 |
+ |
|
| 10 |
+>+ IP地址 |
|
| 11 |
+>+ **名字** 到 **IP地址** 的转换(即使没有DNS服务器的情况下) |
|
| 12 |
+>+ 发现网络中的服务 |
|
| 13 |
+ |
|
| 14 |
+对于第一个要求相关系统和设备可以直接支持的,如动态IP地址分配。 |
|
| 15 |
+第二个要求则可以通过多播(UDP协议向局域网内一组机器发送数据)的方式发送 类似DNS查询的请求,开启着的网络服务收到之后便作出回应,告知自己的名字。 |
|
| 16 |
+第三个要求则通过DNS-SD来实现 |
|
| 17 |
+ |
|
| 18 |
+Bonjour一般的工作模式便是:在同一个局域网中,一方开启服务,通过Bonjour接口将这个服务发布,服务搜索方在服务列表中便可以看到对应的设备的名字,选择设备便可以进行连接了。整个过程无需事先知道服务发布方的IP地址和端口号。 |
|
| 19 |
+我们常用的软件如iTunes的共享,keynote的remote控制或者支持Bonjour协议的打印机都可以看到Bonjour的影子。 |
|
| 20 |
+ |
|
| 21 |
+ |
|
| 22 |
+###二.Bonjour的实现及使用 |
|
| 23 |
+从上面的描述可以看出,Bonjour的用途便是在局域网内发布服务和搜索服务。 |
|
| 24 |
+下面从实现层面讲解Bonjour。 |
|
| 25 |
+ |
|
| 26 |
+|层次|名称| |
|
| 27 |
+|:----|:----| |
|
| 28 |
+|Foundation|NSNetService/NSNetServiceBroswer| |
|
| 29 |
+|CoreFoundation|CFNetService/CFNetServiceBroswer| |
|
| 30 |
+|Low-Level Socket Based API|dns_sd.h(The DNS Service Discovery API)| |
|
| 31 |
+|Multicast DNS Responder|mDNSResponder (开源项目)| |
|
| 32 |
+ |
|
| 33 |
+一般情况下我们使用Foundation这一层接口就可以了,也是最方便的。 |
|
| 34 |
+当然服务方在发布服务之前你得先启好网络服务,比如listening socket创建好,且开始侦听某个端口,关于socket编程的知识可以查看[Socket编程][link-socket] |
|
| 35 |
+ |
|
| 36 |
+[link-socket]:Socket编程.md |
|
| 37 |
+ |
|
| 38 |
+**1.发布服务** |
|
| 39 |
+ |
|
| 40 |
+ netService = [[[NSNetService alloc] initWithDomain:@"" |
|
| 41 |
+ type:@"_test._tcp" |
|
| 42 |
+ name:@"" |
|
| 43 |
+ port:port] autorelease]; |
|
| 44 |
+ if(netService != nil) {
|
|
| 45 |
+ [netService scheduleInRunLoop:[NSRunLoop currentRunLoop] |
|
| 46 |
+ forMode:NSRunLoopCommonModes]; |
|
| 47 |
+ netService.delegate = self; |
|
| 48 |
+ [netService publish]; |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+**2.浏览服务** |
|
| 52 |
+ |
|
| 53 |
++ 创建Service Broswer, 需要指定service type和domain,得和发布服务时候的type对应。还得设置delegate,然后实现其delegate方法,以便发现了服务之后进行处理以及对发现的服务进行获取IP地址和端口的结果进行处理。 |
|
| 54 |
+ |
|
| 55 |
+ |
|
| 56 |
+ testServiceBrowser = [[NSNetServiceBrowser alloc] init]; |
|
| 57 |
+ testServiceBrowser.delegate = self; |
|
| 58 |
+ [testServiceBrowser searchForServicesOfType:@"_test._tcp" inDomain:@""]; |
|
| 59 |
+ |
|
| 60 |
+ |
|
| 61 |
++ 实现Service Broswer 的delegate方法,处理服务增加或减少的事件 |
|
| 62 |
+ |
|
| 63 |
+ |
|
| 64 |
+ //pragma mark NetServiceBroswer Delegate |
|
| 65 |
+ - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser |
|
| 66 |
+ didFindService:(NSNetService*)service |
|
| 67 |
+ moreComing:(BOOL)moreComing {
|
|
| 68 |
+ [netServiceArray addObject:service]; |
|
| 69 |
+ if (!moreComing) {
|
|
| 70 |
+ [serviceTableView reloadData]; |
|
| 71 |
+ } |
|
| 72 |
+ } |
|
| 73 |
+ |
|
| 74 |
+ - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser |
|
| 75 |
+ didRemoveService:(NSNetService*)service |
|
| 76 |
+ moreComing:(BOOL)moreComing {
|
|
| 77 |
+ [netServiceArray removeObject:service]; |
|
| 78 |
+ if (!moreComing) {
|
|
| 79 |
+ [serviceTableView reloadData]; |
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ |
|
| 84 |
++ 连接服务 |
|
| 85 |
+ |
|
| 86 |
+上面发现的Net Service是不带IP地址和端口信息的。 |
|
| 87 |
+从服务列表中选择一个已经发现的服务,进行Resolve,便可以获取服务的详细信息了。 |
|
| 88 |
+ |
|
| 89 |
+ - (IBAction)connect:(id)sender{
|
|
| 90 |
+ NSUInteger selectedRow = [serviceTableView selectedRow]; |
|
| 91 |
+ NSNetService *selectedServiece = [netServiceArray objectAtIndex:selectedRow]; |
|
| 92 |
+ selectedServiece.delegate = self; |
|
| 93 |
+ [selectedServiece resolveWithTimeout:5.0]; |
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+Resolve成功 |
|
| 97 |
+ |
|
| 98 |
+ //NSNetService Delegate |
|
| 99 |
+ - (void)netServiceDidResolveAddress:(NSNetService *)sender{
|
|
| 100 |
+ NSLog(@"service ip:%@ port:%d",sender.address,sender.port); |
|
| 101 |
+ if ([sender getInputStream:&inputStream outputStream:&outputStream]) {
|
|
| 102 |
+ [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] |
|
| 103 |
+ forMode:NSDefaultRunLoopMode]; |
|
| 104 |
+ [outputStream open]; |
|
| 105 |
+ //发送数据 |
|
| 106 |
+ NSData *helloData = [@"Hello" dataUsingEncoding:NSUTF8StringEncoding]; |
|
| 107 |
+ [outputStream write:[helloData bytes] maxLength:[helloData length]]; |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+上面的代码充分利用了输入输出流进行通信。如果你自己的使用socket的连接也是可以的,因为这个时候已经可以获取了对方的IP地址和端口了。 |
相关文章: