Cocoa 网络框架:node
Cocoa 网络框架有三层,最底层的是基于 BSD socket库,而后是 Cocoa 中基于 C 的 CFNetwork,最上面一层是 Cocoa 中 Bonjour。一般咱们无需与 socket 打交道,咱们会使用经 Cocoa 封装的 CFNetwork 和 Bonjour 来完成大多数工做。注:cocoa 不少组件都有两种实现,一种是基于 C 的以 CF 开头的类(CF=Core Foundation),这是比较底层的;另外一种是基于 Obj-C 的以 NS 开头的类(NS=Next Step),这种类抽象层次更高,易于使用。对于网络框架也同样。Bonjour 中 NSNetService 也有对应的 CFNetService,NSInputStream 有对应的 CFInputStream。网络
Bonjour 简介:框架
Bonjour(法语中的你好)是一种可以自动查询接入网络中的设备或应用程序的协议。Bonjour 抽象掉 ip 和 port 的概念,让咱们聚焦于更容易为人类思惟理解的 service。经过 Bonjour,一个应用程序 publish 一个网络服务 service,而后网络中的其余程序就能自动发现这个 service,从而能够向这个 service 查询其 ip 和 port,而后经过得到的 ip 和 port 创建 socket 连接进行通讯。一般咱们是经过 NSNetService 和 NSNetServiceBrowser 来使用 Bonjour 的,前者用于创建与发布 service,后者用于监听查询网络上的 service。dom
同步与异步操做:
异步
大多数网络操做是阻塞模式的,好比连接的创建,等待接收数据,或发送数据给网络另外一端。所以若是咱们不进行异步处理的话,当在进行网络通讯时,咱们的 UI 机会被阻塞。有两种办法来处理阻塞问题:启用多个线程或更有效地利用当前线程。在这个例子中,咱们使用后一种办法,咱们经过 cocoa 提供的 run loop 来作这个事情,其工做原理是:将网络消息看成普通的事件丢到当前的 run loop 中,从而咱们能够异步处理它们。socket
Run loops 简介:async
run loop 是 thread 中的消息处理循环,有事件来则处理,无事件则啥也不作。cocoa 中的 run loop 能够处理用户 UI 消息,网络链接消息,timer 消息等。咱们也能够添加其余的消息来源,如 socket 和 stream,从而让 run loop 也能够处理它们。tcp
经过NSNetService发布socket oop
// 初始化服务,指定服务的域,类型,名称和端 NSNetService *service = [[NSNetService alloc] initWithDomain:@"local." type:@"_WE._tcp" name:@"WE_iPhone" port:0]; service.delegate = self; NSData *data = [NSNetService dataFromTXTRecordDictionary:@{@"node":@"http://www.we.com"}]; [service setTXTRecordData:data]; self.service = service; // 发布 [service publish];
若是须要发布一个服务,须要在发布服务以前准备好服务并启动它。不过NSNetService的publish方法并不依赖它所发布的服务,无论服务是否准备好,是否启动,NSNetService的publish均可以成功将服务发布出去,只不过服务发布出去后其它使用这个服务的客户端会发现这个发布出来的服务是个无效服务。ui
发布服务成功以后,开始查找。
NSNetServiceDelegate代理
/* * 即将发布 */ - (void)netServiceWillPublish:(NSNetService *)sender { NSLog(@"---------------netServiceWillPublish"); } /* * 发布成功 */ - (void)netServiceDidPublish:(NSNetService *)sender {
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(concurrentQueue, ^{ NSRunLoop *mainRunLoop = [NSRunLoop currentRunLoop]; NSNetServiceBrowser* browser = [[NSNetServiceBrowser alloc] init]; browser.delegate = self; [browser scheduleInRunLoop:mainRunLoop forMode:NSRunLoopCommonModes]; [browser searchForServicesOfType:@"_WE._tcp" inDomain:@"local."]; [mainRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30]]; });
}
/* * 发布失败,返回错误 */ - (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary<NSString *, NSNumber *> *)errorDict { } /* * 开始解析 */ - (void)netServiceWillResolve:(NSNetService *)sender { NSLog(@"--------------netServiceWillResolve"); } /* * 解析成功 */ - (void)netServiceDidResolveAddress:(NSNetService *)sender { NSLog(@"netServiceDidResolveAddress---------=%@ =%@ =%@",service.name,service.addresses,service.hostName); } /* * 解析服务失败,解析出错 */ - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary<NSString *, NSNumber *> *)errorDict { NSLog(@"------------netService didNotResolve =%@",errorDict); } /* * 中止服务 */ - (void)netServiceDidStop:(NSNetService *)sender { NSLog(@"--------------netServiceDidStop"); } /* * 服务数据更新 */ - (void)netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data { NSLog(@"--------------netService didUpdateTXTRecordData"); } /* * 链接成功输出流和输入流 */ - (void)netService:(NSNetService *)sender didAcceptConnectionWithInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream { NSLog(@"--------------netService didAcceptConnectionWithInputStream"); }
使用NSNetService框架中的NSNetServiceBrowser类去发现本地服务
接着使用NSNetServiceBrowser实例的searchForServicesOfType方法查找服务,方法中能够指定须要查找的服务类型和查找的域。如下样例查找“local.”域中的“_WE._tcp”服务
// 发现本地服务 NSNetServiceBrowser* browser = [[NSNetServiceBrowser alloc] init]; browser.delegate = self; [browser scheduleInRunLoop:mainRunLoop forMode:NSRunLoopCommonModes]; [browser searchForServicesOfType:@"_WE._tcp" inDomain:@"local."]; [mainRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30]];
在“NSNetServiceBrowserDelegate”的如下方法中响应“didFindService”事件,就是找到服务的事件。其中的netService参数就是找到的服务,在netService参数中能够获得服务地址,服务主机名等信息
实现NSNetServiceBrowser实例的代理
/* * 即将查找服务 */ - (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser { NSLog(@"-----------------netServiceBrowserWillSearch"); } /* * 中止查找服务 */ - (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser { NSLog(@"-----------------netServiceBrowserDidStopSearch"); } /* * 查找服务失败 */ - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary<NSString *, NSNumber *> *)errorDict { NSLog(@"----------------netServiceBrowser didNotSearch"); } /* * 发现域名服务 */ - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindDomain:(NSString *)domainString moreComing:(BOOL)moreComing { NSLog(@"---------------netServiceBrowser didFindDomain"); } /* * 发现客户端服务 */ - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing { NSLog(@"didFindService---------=%@ =%@ =%@",service.name,service.addresses,service.hostName);
[aNetService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; aNetService.delegate = self; [aNetService resolveWithTimeout:6]; CFRunLoopRun();
} /* * 域名服务移除 */ - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing { NSLog(@"---------------netServiceBrowser didRemoveDomain"); } /* * 客户端服务移除 */ - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didRemoveService:(NSNetService *)service moreComing:(BOOL)moreComing { NSLog(@"---------------netServiceBrowser didRemoveService"); }
在发现客户端里,设置NSNetService代理,在解析成功-(void)netServiceDidResolveAddress;方法里解析
能够拿到名称、类型、域、主机名、ip地址
/* Returns the name of the discovered or published service. */ @property (readonly, copy) NSString *name; /* Returns the type of the discovered or published service. */ @property (readonly, copy) NSString *type; /* Returns the domain of the discovered or published service. */ @property (readonly, copy) NSString *domain; /* Returns the DNS host name of the computer hosting the discovered or published service. If a successful resolve has not yet occurred, this method will return nil. */ @property (nullable, readonly, copy) NSString *hostName; /* The addresses of the service. This is an NSArray of NSData instances, each of which contains a single struct sockaddr suitable for use with connect(2). In the event that no addresses are resolved for the service or the service has not yet been resolved, an empty NSArray is returned. */ @property (nullable, readonly, copy) NSArray<NSData *> *addresses;
当解析成功客户端的IP、端口、类型、域以后,就能够经过创建链接进行通讯。