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、端口、類型、域以後,就能夠經過創建鏈接進行通訊。