智能交互(二) - bonjour的基本使用

如果你進來了,點下關(guān)注行不行_
在使用這個bonjour協(xié)議之前, 先談?wù)剝蓚€問題?

bonjour是什么?##

bonjour是蘋果公司發(fā)布的一個基于ZEROCONF工作組(IETF下屬小組)的工作,用于實現(xiàn)零配置網(wǎng)絡(luò)聯(lián)網(wǎng)的解決方案。Bonjour是基于IP層協(xié)議的。簡單點說,就是 某個組織發(fā)明了一套解決方案,這套協(xié)議能夠不需要 (復(fù)雜的配置),即可互相發(fā)現(xiàn)彼此的解決方案。

為什么要使用它?##

因為如果不用他,就要經(jīng)過一套(復(fù)雜的配置),才能連接設(shè)備。簡單解釋下,此前的配置是怎樣復(fù)雜法。

連接設(shè)備, 首先想到就是 IP, 或者 UDP廣播, 另外一邊監(jiān)聽,從而連接起來.

1, IP
通過IP連接, 就需要知道雙方的IP地址, 端口, 也就是說, 在連接的時候,
查詢IP與, 這是一個步驟
輸入IP與端口,等待連接, 當(dāng)然,這兩步兩端都要做, 然后進行連接,
這用戶體驗被虐的體無完膚

2,UDP廣播
首先, 手機不斷發(fā)送UDP廣播, 硬件設(shè)備接收, 然后開始連接
有什么問題?

-> 網(wǎng)絡(luò)阻塞問題。因為UDP廣播會對同一本地網(wǎng)絡(luò)的所有Host都發(fā)送信息。過于密集的發(fā)送,有可能會造成網(wǎng)絡(luò)的堵塞。
-> 而且基于Socket實現(xiàn),我們還要考慮網(wǎng)絡(luò)的穩(wěn)定性,Socket斷開與重連等情況。(代碼量增大)
-> 由于UDP廣播的間隔時間與不穩(wěn)定性,導(dǎo)致我們獲取設(shè)備的速度不快和穩(wěn)定不足。
-> 由于耗電, 影響用戶體驗, APP整體質(zhì)量

解決這些問題, 需要大量的代碼量,與實現(xiàn)邏輯結(jié)構(gòu)

bonjouze怎么用?##

  1. 尋址(分配IP地址給主機)
  2. 命名(使用名字而不是IP地址來代表主機)
  3. 服務(wù)搜索(自動在網(wǎng)絡(luò)搜索服務(wù))

->尋址。###

一個在網(wǎng)絡(luò)中的設(shè)備需要有一個自己的IP。有了IP地址,我們才能基于IP協(xié)議進行通信。
實現(xiàn)原理: Bonjour協(xié)議的尋址依賴于IP層協(xié)議。
對于IPV6標準,IP協(xié)議已經(jīng)包括了自動尋找IP地址的功能。但是目前仍然普遍使用的IPV4 不包含本地鏈路尋址功能。那么解決方案就是在本地網(wǎng)絡(luò)選擇一個隨機的IP地址進行測試,如果已經(jīng)被占用,則繼續(xù)挑選另外一個地址。

-> 命名。###

我們不想通過冷冰冰的IP地址來作為我們服務(wù)的標志。我們想為我們的服務(wù)取一個名字。就像打印機一樣,我們希望能在網(wǎng)絡(luò)發(fā)現(xiàn)它的時候,是以一個比如“二樓的打印機”這樣的標志,而不是一串冷冰冰“10.9.166.45”的IP地址。

就像我們希望發(fā)現(xiàn)我們的需要調(diào)試的iOS設(shè)備的時候,能夠知道它是“Mango's iPhone7”、因此,我們需要給我們的設(shè)備和服務(wù)命名。

我們還希望能夠通過名字找到服務(wù)準確的IP地址,就像在瀏覽器輸入"www.qq.com"一樣,DNS服務(wù)器會自動幫我導(dǎo)向正確的網(wǎng)站IP地址。

而Bonjour,正是幫我們實現(xiàn)了命名和解析的功能。保證了我們服務(wù)的名字在本地網(wǎng)絡(luò)是唯一的,并且把別人對我們名字的查詢指向正確的IP地址和端口。

實現(xiàn)原理:
我們在這里拋開復(fù)雜的RFC 6762規(guī)范,用簡潔的語言介紹一下原理。

指定名字:
用戶在注冊一個名字的時候,設(shè)備向本地網(wǎng)絡(luò)發(fā)送查詢來確定名字是否選中。如果用戶提供的名字已經(jīng)被使用,則Bonjour會自動重命名我們的服務(wù)。例如我們注冊名字為"Mango's iPhone7"已經(jīng)被使用,那么Bonjour可能會幫我們?nèi)?Mango's iPhone7-1"的名字。

解析名字 :
如果有用戶發(fā)出一個查詢,說我想找名字叫"Mango's iPhone7"的設(shè)備,則本地網(wǎng)絡(luò)收到請求的設(shè)備看看自己是不是被請求了,如果是的話,則返回正確的IP地址,端口。

responder :
需要了解的是而Bonjour在系統(tǒng)級別上添加了一個mDNSResponder服務(wù)來處理請求和發(fā)送回復(fù),從系統(tǒng)級層面上處理,我們就無需在應(yīng)用內(nèi)自己監(jiān)聽和返回IP地址,只需到系統(tǒng)內(nèi)注冊服務(wù)即可。減少了我們應(yīng)用的工作量和提高了穩(wěn)定性。就好像APNS在iOS上幫助我們維持一個系統(tǒng)級別的長連接。

-> 服務(wù)搜索。###

我們還需要搜索網(wǎng)絡(luò)上可用的設(shè)備和服務(wù)來查看可用的服務(wù)。Bonjour幫助我們,只需指定所需服務(wù)的類型即可收到本地網(wǎng)絡(luò)上可用的設(shè)備列表。

實現(xiàn)原理:

設(shè)備在本地網(wǎng)絡(luò)發(fā)出請求,說我需要"XXX"類型的服務(wù),例如:我要打印機服務(wù)。所有打印機服務(wù)的設(shè)備回應(yīng)自己的名字。

Cocoa中的bonjouze?##

Bonjour在Cocoa世界里的實現(xiàn)Stack: 調(diào)用服務(wù)


Cocoa 中實現(xiàn)bonjouze的 API :

下面簡述一下整個流程 :

// 這個是wifi模塊制造廠商
if (!self.mylink) {
      self.mylink = [[MYLINK alloc] initWithDelegate:self];
}

// 將wifi賬號密碼保存,發(fā)送給智能硬件. 發(fā)送的方法,由wifi模塊框架提供
NSMutableDictionary *wifiConfig = [NSMutableDictionary dictionaryWithCapacity:20];
NSData *ssidData = [self.tfdSSID.text dataUsingEncoding:NSUTF8StringEncoding];
[wifiConfig setObject:ssidData forKey:KEY_SSID];
[wifiConfig setObject:self.tfdPWD.text forKey:KEY_PASSWORD];
[wifiConfig setObject:[NSNumber numberWithBool:YES] forKey:KEY_DHCP];
// 注冊的功能, 是由wifi模組提供
[self.easylink prepareEasyLink_withFTC:wifiConfig info:nil mode:EASYLINK_V2_PLUS];
[self.easylink transmitSettings];

好了,我們已經(jīng)注冊了服務(wù),接下來就是進行檢索了.
定義兩個變量.

// NSNetService 代表一個服務(wù)。 NSNetServiceBrowser 用于搜索服務(wù)。
@property(strong, nonatomic) NSNetServiceBrowser *brower;
@property(strong, nonatomic) NSNetService *service;

搜索服務(wù) :

// 假如檢索數(shù)據(jù)不成功, 請先停止上一次的檢索
[self.brower stop];
// @"_mylink._tcp" 這個名字是模塊上提供的服務(wù)名字(其實告訴它,喂,你搜這個名的服務(wù)!)
// 從網(wǎng)上看到一個好軟件 Discovery - Bonjour Brower ,上AppStore搜索一下你就能搜索到,能查詢服務(wù)
 [self.brower searchForServicesOfType:@"_mylink._tcp" inDomain:@"local."];

搜索之后,哪里反饋信息?

// 當(dāng)檢索到指定名的服務(wù)時就會調(diào)用的代理方法,想獲得更細致的數(shù)據(jù)就必須通過它來做下一步動作
-(void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing{}
// 發(fā)生錯誤的時候調(diào)用這個方法
-(void)netService:(NSNetService *)sender didNotResolve:(NSDictionary<NSString *,NSNumber *> *)errorDict{}
// 想要獲得更細致的數(shù)據(jù),那么就需要調(diào)用這個方法,不過,首先先利用(第一個代理方法)。
-(void)netServiceDidResolveAddress:(NSNetService *)sender{}

詳細使用 :

- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing{
    //為service設(shè)置代理,無法直接在此代理中得到地址,似乎必須通過代理回調(diào)
    NSString *serviceStr = [service.name lowercaseString];
    NSRange serviceRange = [serviceStr rangeOfString:@"#"];
    NSString *maccode = [serviceStr substringWithRange:NSMakeRange(serviceRange.location+3, 4)];
    echo(@"%@",maccode);
    // 蘋果同一時間里只給你提供一個服務(wù)的詳細數(shù)據(jù)
    if ([maccode isEqualToString:[self.tfdCode.text lowercaseString]] ) {
        self.service = service;
        self.service.delegate = self;
        //使用它,在下一個代理回調(diào)內(nèi)得到數(shù)據(jù)
        [self.service resolveWithTimeout:1.0];    }
}

-(void)netServiceDidResolveAddress:(NSNetService *)sender{
    // 這個NSNetService, 就是代表了一個服務(wù)
    NSData *data = [sender TXTRecordData];
    NSDictionary *dict = [NSNetService dictionaryFromTXTRecordData:data];
    NSData *str = [dict objectForKey:@"MAC"];
    NSString *oldmacadress = [[NSString alloc]initWithData:str encoding:NSUTF8StringEncoding];
    //需要硬件的mac地址,那么切記字典中的數(shù)據(jù)并不是立馬就能用的,必須再做處理。當(dāng)然, 其他也是這樣
    NSString *macadress = [oldmacadress stringByReplacingOccurrencesOfString:@":" withString:@""];
    if (macadress.length < 8) {
        return;
    }
    if (self.timer) {
        [self.timer invalidate];
    }
    _Mac = macadress;
    
    // 處理完成后, 一定要關(guān)閉連接
    [self stopMyLink];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容