青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

羅朝輝(飄飄白云)

關(guān)注嵌入式操作系統(tǒng),移動(dòng)平臺(tái),圖形開發(fā)。-->加微博 ^_^

  C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  85 隨筆 :: 0 文章 :: 169 評(píng)論 :: 0 Trackbacks
深入淺出 Cocoa 之 Bonjour 網(wǎng)絡(luò)編程
CC許可,轉(zhuǎn)載請(qǐng)注明出處

本文通過(guò)使用 Bonjour 在 Mac 上實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的服務(wù)器/客戶端聊天程序,以演示如何使用 Bonjour 進(jìn)行網(wǎng)絡(luò)編程,其中使用到 CFSokcet, NSNetService/NSNetServiceBrowser, NSInputStream/NSOutStream 的用法。本文高度參考自Tutorial: Networking and Bonjour on iPhone在那篇文章中實(shí)現(xiàn)了 iPhone 版本的聊天程序,源代碼采用 MIT 開源協(xié)議,所有本例子中的代碼亦使用 MIT 開源協(xié)議。E 文較好的童鞋建議閱讀原文。

代碼下載:點(diǎn)擊這里
效果圖如下:


Cocoa 網(wǎng)絡(luò)框架:
Cocoa 網(wǎng)絡(luò)框架有三層,最底層的是基于 BSD socket庫(kù),然后是 Cocoa 中基于 C 的 CFNetwork,最上面一層是 Cocoa 中 Bonjour。通常我們無(wú)需與 socket 打交道,我們會(huì)使用經(jīng) Cocoa 封裝的 CFNetwork 和 Bonjour 來(lái)完成大多數(shù)工作。注:cocoa 很多組件都有兩種實(shí)現(xiàn),一種是基于 C 的以 CF 開頭的類(CF=Core Foundation),這是比較底層的;另一種是基于 Obj-C 的以 NS 開頭的類(NS=Next Step),這種類抽象層次更高,易于使用。對(duì)于網(wǎng)絡(luò)框架也一樣。Bonjour 中 NSNetService 也有對(duì)應(yīng)的 CFNetService,NSInputStream 有對(duì)應(yīng)的 CFInputStream。

Sockets vs Streams:
Socket 相當(dāng)于一條通信信道,應(yīng)用程序通過(guò)創(chuàng)建 socket,然后使用這個(gè) socket 連接到其他應(yīng)用程序進(jìn)行數(shù)據(jù)交換。我們可以通過(guò)同一個(gè) socket 來(lái)發(fā)送數(shù)據(jù)或者接收數(shù)據(jù)。每個(gè) socket 有一個(gè) ip 地址和 port(通信端口,介于 1 ~ 65535之間)。

Stream 是傳送數(shù)據(jù)的單向通道,正因?yàn)槭菃蜗虻模晕覀冇休斎?輸出兩種 streams:instream/outstream。stream 只是臨時(shí)緩存數(shù)據(jù),我們需要將它與文件或內(nèi)存綁定,從而可以從/向文件或內(nèi)存中讀/寫數(shù)據(jù)。在這個(gè)教程中,我們使用 stream 結(jié)合 socket 在網(wǎng)絡(luò)上傳送和接收數(shù)據(jù)。

Bonjour 簡(jiǎn)介:
Bonjour(法語(yǔ)中的你好)是一種能夠自動(dòng)查詢接入網(wǎng)絡(luò)中的設(shè)備或應(yīng)用程序的協(xié)議。Bonjour 抽象掉 ip 和 port 的概念,讓我們聚焦于更容易為人類思維理解的 service。通過(guò) Bonjour,一個(gè)應(yīng)用程序 publish 一個(gè)網(wǎng)絡(luò)服務(wù) service,然后網(wǎng)絡(luò)中的其他程序就能自動(dòng)發(fā)現(xiàn)這個(gè) service,從而可以向這個(gè) service 查詢其 ip 和 port,然后通過(guò)獲得的 ip 和 port 建立 socket 鏈接進(jìn)行通信。通常我們是通過(guò) NSNetService 和 NSNetServiceBrowser 來(lái)使用 Bonjour 的,前者用于建立與發(fā)布 service,后者用于監(jiān)聽查詢網(wǎng)絡(luò)上的 service。
同步與異步操作:
大多數(shù)網(wǎng)絡(luò)操作是阻塞模式的,比如鏈接的建立,等待接收數(shù)據(jù),或發(fā)送數(shù)據(jù)給網(wǎng)絡(luò)另一端。因此如果我們不進(jìn)行異步處理的話,當(dāng)在進(jìn)行網(wǎng)絡(luò)通信時(shí),我們的 UI 機(jī)會(huì)被阻塞。有兩種辦法來(lái)處理阻塞問(wèn)題:?jiǎn)⒂枚鄠€(gè)線程或更有效地利用當(dāng)前線程。在這個(gè)例子中,我們使用后一種辦法,我們通過(guò) cocoa 提供的 run loop 來(lái)做這個(gè)事情,其工作原理是:將網(wǎng)絡(luò)消息當(dāng)作普通的事件丟到當(dāng)前的 run loop 中,從而我們可以異步處理它們。

Run loops 簡(jiǎn)介:
run loop 是 thread 中的消息處理循環(huán),有事件來(lái)則處理,無(wú)事件則啥也不做。cocoa 中的 run loop 可以處理用戶 UI 消息,網(wǎng)絡(luò)連接消息,timer 消息等。我們也可以添加其他的消息來(lái)源,如 socket 和 stream,從而讓 run loop 也可以處理它們。
程序框架:
理論介紹得差不多了,更多細(xì)節(jié),請(qǐng)翻閱官方文檔。下面我們打開 MacChatty 來(lái)看看具體實(shí)現(xiàn)。

從上圖可以清晰地看出,程序分為三個(gè)主要模塊:UI模塊,邏輯模塊,網(wǎng)絡(luò)模塊。下面我們打開工程,看看代碼實(shí)現(xiàn):


從工程圖可以看出,代碼結(jié)構(gòu)相當(dāng)清晰,所有的類被分為四個(gè) group:
Networking:網(wǎng)絡(luò)相關(guān)的代碼,包括 socket 的創(chuàng)建,連接的建立,service 的 publish 和 browser;
Business Logic:業(yè)務(wù)邏輯相關(guān)代碼。在這個(gè)例子中,我們通過(guò) room service 來(lái)提供聊天服務(wù)。我們通過(guò)建立一個(gè) localroom 來(lái)創(chuàng)建服務(wù)器,并發(fā)布一個(gè) room service,客戶端(remoteroom)能夠連接到一個(gè)已有的 room service,從而加入該 room 進(jìn)行對(duì)話活動(dòng)。
UI:在這個(gè)例子中,UI 很簡(jiǎn)單,只有兩個(gè) view,一個(gè)顯示當(dāng)前網(wǎng)絡(luò)中的 service 列表,另一個(gè)顯示 room,以及在該 room service 上進(jìn)行的對(duì)話。
Misc:一個(gè)輔助類,用于存儲(chǔ)用戶設(shè)定名稱。

網(wǎng)絡(luò)類:
Server class:創(chuàng)建 server,并發(fā)布 service;
Connection class:決議 service;與服務(wù)器建立連接;通過(guò) socket stream 交換數(shù)據(jù);
ServerBrowser class:查詢可用的 service;

Room類:
Room class: Room 基類
LocalRoom class: 創(chuàng)建服務(wù)器,發(fā)布 service,相應(yīng)客戶端的連接請(qǐng)求
RemoteRoom:  連接到服務(wù)器已有的 service,

網(wǎng)絡(luò)數(shù)據(jù)傳輸過(guò)程:
從上圖可以看出,數(shù)據(jù)從 A 的邏輯層,經(jīng) outgoing buffer 寫入 write stream,然后經(jīng) socket 通過(guò)網(wǎng)絡(luò)傳輸?shù)?B 的網(wǎng)絡(luò)層,然后 B 端的  read stream 從 socket 中讀取數(shù)據(jù),寫入 incoming buffer,然后在 B 的邏輯層以及 UI 上顯示出來(lái)。
用戶交互操作都在 UI layer 上進(jìn)行,當(dāng)用戶通過(guò) broadcastChatMessage:fromUser: 發(fā)送一條聊天信息,由邏輯層來(lái)決定是發(fā)送給服務(wù)器(由 Remote room 處理),還是發(fā)送給連接到服務(wù)器自身的所有客戶端(由 Local room 處理)。當(dāng)從網(wǎng)絡(luò)連接接收到一條聊天信息時(shí),邏輯層會(huì)得到通知,客戶端只會(huì)簡(jiǎn)單地將消息顯示在 UI 上,而服務(wù)器首先將收到的聊天信息轉(zhuǎn)發(fā)給所有連接到它的客戶端,然后將該信息在 UI 上顯示出來(lái)。

Socket+Streams+Buffers=Connection
Connection 類對(duì)一些的交互進(jìn)行了封裝:
兩個(gè) socket stream,一個(gè)用來(lái)寫入,一個(gè)用來(lái)讀取;兩塊 data buffer,每個(gè) socket stream 對(duì)應(yīng)一個(gè) data buffer;以及各種控制 flag 和值
因?yàn)?stream 是單向的,所以我們需要為每一個(gè) socket 建立兩個(gè) stream,一個(gè)用來(lái)從 socket 讀取數(shù)據(jù),一個(gè)用來(lái)向 socket 寫入數(shù)據(jù)。我們?cè)?connect 和 setupSocketStreams 中初始化它們。
在本例中,我們通過(guò)兩種方式來(lái)創(chuàng)建 socket:
1,(客戶端)通過(guò)連接到制定 ip 和 port 的服務(wù)器;
2,(服務(wù)器)通過(guò)接收來(lái)自客戶端的連接請(qǐng)求,在這種情況下,OS 會(huì)自動(dòng)創(chuàng)建一個(gè)用于響應(yīng)的 socket,并通過(guò) native socket handle 傳遞給我們使用;

無(wú)論 socket 是由哪種方式建立的,我們都是通過(guò)相同的代碼 setupSocketStreams 來(lái)初始化 stream。

創(chuàng)建 server
聊天至少需要同時(shí)運(yùn)行兩個(gè) MacChatty 終端,其中至少有一個(gè)作為服務(wù)器,其他終端才能作為客戶端連接到服務(wù)器進(jìn)行對(duì)話。作為服務(wù)器的終端,需要?jiǎng)?chuàng)建一個(gè) socket 來(lái)監(jiān)聽(listen)其他終端的連接請(qǐng)求(請(qǐng)參考 Sever class 中的 listeningSocket)。這項(xiàng)工作是在 Server 類中的 createServer 中完成的。

客戶端如何知道怎樣連接到服務(wù)器呢?每一個(gè)網(wǎng)絡(luò)終端必須有獨(dú)一無(wú)二的 ip 和 port,ip 地址是由動(dòng)態(tài)獲取的或由用戶設(shè)定的,因此我們?cè)谶@里無(wú)需操心 ip 地址問(wèn)題,因此在代碼中我們使用了 INADDR_ANY。那又如何設(shè)定我們想要監(jiān)聽的 port 呢?一些服務(wù)必須監(jiān)聽約定的 port 才能工作,比如 80,20, 21等端口都是有約定用途的。在這里我們把端口設(shè)定問(wèn)題交給 OS 來(lái)處理,OS 會(huì)為我們?cè)O(shè)定一個(gè)沒有被占用的 port。為了實(shí)現(xiàn)這個(gè)目的,我們傳入 port 為 0。為了讓其他客戶端能夠連接到服務(wù)器,我們需要告知其他客戶端服務(wù)器實(shí)際使用的 port,因此,我們?cè)?createServer 方法 PART 3中獲取實(shí)際使用 port。
    //// PART 3: Find out what port kernel assigned to our socket
    //
    
// We need it to advertise our service via Bonjour
    NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease];
    
    
// Convert socket data into a usable structure
    struct sockaddr_in socketAddressActual;
    memcpy(
&socketAddressActual, [socketAddressActualData bytes], [socketAddressActualData length]);
    
    self.port 
= ntohs(socketAddressActual.sin_port);

然后在 PART 4 中,我們將 listening socket 注冊(cè)為 application run loop 的消息源,這樣當(dāng)有新連接到來(lái)的時(shí)候, OS 就會(huì)調(diào)用 serverAcceptCallback 這個(gè)回調(diào)函數(shù)通知我們。
    //// PART 4: Hook up our socket to the current run loop
    //
    CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
    CFRunLoopSourceRef runLoopSource 
= CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0);
    CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes);
    CFRelease(runLoopSource);

在 serverAcceptCallback 回調(diào)處理中,我們創(chuàng)建一個(gè)新的 Connection 對(duì)象,然后將它與 OS 自動(dòng)創(chuàng)建的響應(yīng)新連接的 socket 綁定起來(lái)。然后再將這個(gè) Connection 對(duì)象傳遞給 Server delegate。

通過(guò) Bonjour 發(fā)布服務(wù)
Bonjour 并非在網(wǎng)絡(luò)查找服務(wù)的唯一途徑,但它是最容易使用的方法之一。我們?cè)?publishService 方法中創(chuàng)建一個(gè) NSNetService 對(duì)象來(lái)發(fā)布服務(wù)。我們根據(jù)服務(wù)類型在網(wǎng)絡(luò)查找感興趣的服務(wù),本聊天服務(wù)使用“_chatty._tcp.”作為服務(wù)類型。在同一網(wǎng)絡(luò)中,服務(wù)類型名必須唯一,這樣才能精準(zhǔn)定位服務(wù),而不至于引發(fā)沖突。

Bonjour 操作也如 socket 一樣需要異步進(jìn)行,以避免長(zhǎng)時(shí)間阻塞主線程。因此在實(shí)際發(fā)布服務(wù)時(shí),我們將發(fā)布任務(wù)交給當(dāng)前 run loop 去調(diào)度,然后設(shè)定其 delegate,由 delegate 來(lái)處理相關(guān)事件:“Publishing succeeded”, “Publishing failed”等。
- (BOOL) publishService
{
    
// come up with a name for our chat room
    NSString* chatRoomName = [NSString stringWithFormat:@"%@'s chat room", [[AppConfig sharedInstance] name]];
    
    
// create new instance of netService
     self.netService = [[NSNetService alloc] initWithDomain:@"" type:@"_chatty._tcp." name:chatRoomName port:self.port];
    
if (self.netService == nil)
        
return NO;
    
    
// Add service to current run loop
    [self.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    
    
// NetService will let us know about what's happening via delegate methods
    [self.netService setDelegate:self];
    
    
// Publish the service
    [self.netService publish];
    
    
return YES;
}

通過(guò) Bonjour 查詢服務(wù)
我們?cè)?ServerBrowser 類中實(shí)現(xiàn) Bonjour 查詢網(wǎng)絡(luò)服務(wù)的功能。我們創(chuàng)建一個(gè) NSNetServiceBrowser 對(duì)象來(lái)查詢類型為 “_chatty._tcp.” 的服務(wù)。當(dāng)前網(wǎng)絡(luò)中發(fā)現(xiàn)有服務(wù)被添加到或移除時(shí),NSNetServiceBrowser 的 delegate 即我們的 ServerBrowser 就能得到通知,以進(jìn)行相應(yīng)的邏輯處理:更新服務(wù)列表,刷新 UI 等。

通過(guò) Bonjour 決議服務(wù)
當(dāng)用戶選擇其中一個(gè) chat room,并加入其中時(shí),客戶端將會(huì)連接到發(fā)布該 chat room 服務(wù)的服務(wù)器。這個(gè)連接過(guò)程在 ChattyViewController 類的 joinChatRoom: 方法中實(shí)現(xiàn)。首選我們通過(guò)選擇的 NSNetService 發(fā)送 resolveWithTimeout: 消息來(lái)進(jìn)行決議應(yīng)該連接到哪個(gè)服務(wù)器(請(qǐng)參考 Connection 類的 connect 方法中最后一種情形),同時(shí)設(shè)定 NSNetService 的 delegate 來(lái)響應(yīng)決議相關(guān)的事件:didNotResolve: 和 netServiceDidResolveAddress:。當(dāng)決議完成之后,在 netServiceDidResolveAddress: 方法中,我們可以創(chuàng)建用于數(shù)據(jù)傳輸?shù)?stream 了。
// Called when net service has been successfully resolved
- (void)netServiceDidResolveAddress:(NSNetService *)sender
{
    
if ( sender != netService ) {
        
return;
    }
    
    
// Save connection info
    self.host = netService.hostName;
    self.port 
= netService.port;
    
    
// Don't need the service anymore
    self.netService = nil;
    
    
// Connect!
    if ( ![self connect] ) {
        [
delegate connectionAttemptFailed:self];
        [self close];
    }
}

至此,Bonjour 網(wǎng)絡(luò)編程介紹就結(jié)束了,代碼中的注釋相當(dāng)詳細(xì),細(xì)節(jié)就不多羅嗦了。

參考資料:
Tutorial: Networking and Bonjour on iPhone:
http://mobileorchard.com/tutorial-networking-and-bonjour-on-iphone/
posted on 2011-09-25 15:43 羅朝輝 閱讀(2561) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)相關(guān)Cocoa 開發(fā)
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久精彩视频| 在线观看91精品国产麻豆| 亚洲欧美中文字幕| 欧美成人综合网站| 麻豆av福利av久久av| 国产美女在线精品免费观看| 亚洲精品国产精品国自产观看浪潮| 韩国欧美一区| 欧美在线视频不卡| 久久久99久久精品女同性| 国产精品超碰97尤物18| 中日韩美女免费视频网址在线观看| 一区二区三区视频观看| 欧美区视频在线观看| 亚洲欧洲精品天堂一级| 99视频精品在线| 欧美日精品一区视频| 一本久久a久久精品亚洲| 亚洲影院免费| 国产区精品视频| 久久国产日本精品| 女女同性精品视频| 亚洲精品中文字幕在线观看| 欧美久久久久久| 一本色道久久99精品综合| 亚洲在线黄色| 国产三级欧美三级日产三级99| 午夜精品国产精品大乳美女| 久久天堂av综合合色| 最新日韩在线视频| 欧美日韩亚洲一区二区三区| 在线视频欧美精品| 久久精品亚洲热| 亚洲国产欧美一区二区三区同亚洲| 鲁大师成人一区二区三区| 日韩视频不卡中文| 久久精品久久99精品久久| 在线精品一区二区| 欧美日韩国产欧美日美国产精品| 亚洲小视频在线| 欧美成人第一页| 中文欧美字幕免费| 国内精品久久久久影院色| 欧美a级理论片| 亚洲午夜高清视频| 欧美aⅴ99久久黑人专区| 中文av一区二区| 韩国三级电影一区二区| 欧美日韩三级视频| 欧美一级视频精品观看| 亚洲激情在线观看| 欧美一区二区在线视频| 亚洲丶国产丶欧美一区二区三区 | 欧美va亚洲va国产综合| 亚洲美女av网站| 国产乱码精品1区2区3区| 老司机精品福利视频| 亚洲视频999| 欧美成人中文字幕在线| 香蕉成人久久| 亚洲精品一区在线| 国产亚洲一区二区三区| 欧美激情第三页| 久久gogo国模裸体人体| 一区二区三区国产精品| 欧美激情一区二区三区| 国产精品综合色区在线观看| 久久亚洲综合色| 亚洲午夜电影| 亚洲人成在线播放| 久久一区视频| 欧美一区二区网站| 亚洲一二区在线| 亚洲日韩欧美视频| 黄色亚洲在线| 国产精品永久免费视频| 欧美日韩亚洲激情| 欧美激情精品久久久| 久久久久国内| 午夜精品剧场| 亚洲视频免费在线观看| 亚洲精品久久久久久久久| 免费美女久久99| 久久久噜噜噜| 久久丁香综合五月国产三级网站| 99国产精品国产精品久久| 亚洲国产精品123| 伊人久久男人天堂| 精品二区视频| 国产一区二区三区久久| 国产精品一级二级三级| 国产精品videossex久久发布| 欧美激情一区二区三区蜜桃视频| 久久综合色播五月| 久久久国产精品一区| 欧美亚洲综合另类| 小处雏高清一区二区三区| 亚洲综合视频网| 亚洲欧美国产精品专区久久| 一区二区精品在线| 艳妇臀荡乳欲伦亚洲一区| 99精品国产在热久久| 亚洲乱码国产乱码精品精天堂| 欧美国产日本在线| 欧美激情视频一区二区三区不卡| 免费视频最近日韩| 女生裸体视频一区二区三区| 男女激情久久| 亚洲成人自拍视频| 亚洲人成在线观看| 日韩视频欧美视频| 中文日韩电影网站| 欧美一区1区三区3区公司| 欧美中文在线观看国产| 久久久夜夜夜| 欧美黄色影院| 欧美午夜免费影院| 国产欧美日韩综合一区在线播放| 国产情人节一区| 在线免费观看欧美| 亚洲精品精选| 亚洲免费中文| 久久久久天天天天| 欧美黄色aa电影| 99re热这里只有精品免费视频| 宅男噜噜噜66一区二区| 欧美影院一区| 欧美精品xxxxbbbb| 国产精品入口| 在线色欧美三级视频| 99精品国产一区二区青青牛奶| 亚洲一区二区三区四区视频| 久久精品噜噜噜成人av农村| 欧美成人精品一区| 野花国产精品入口| 久久精品国产综合精品| 欧美国产精品久久| 国产精品乱码妇女bbbb| 1024成人| 亚洲愉拍自拍另类高清精品| 久久免费视频观看| 亚洲精品国产日韩| 午夜在线视频观看日韩17c| 蜜臀久久99精品久久久久久9| 欧美日韩一区二区三区免费| 国产亚洲一二三区| 夜夜爽99久久国产综合精品女不卡| 久久aⅴ国产欧美74aaa| 欧美一区二区三区精品| 美女啪啪无遮挡免费久久网站| 亚洲国产成人精品久久| 亚洲图片欧美午夜| 久久久午夜视频| 国产精品福利网站| 亚洲国产成人av好男人在线观看| 亚洲性色视频| 欧美电影免费观看高清| 亚洲欧美成人| 欧美伦理视频网站| 在线观看欧美一区| 先锋a资源在线看亚洲| 亚洲国产一区二区三区青草影视| 亚洲欧美日韩一区在线| 欧美另类极品videosbest最新版本| 国产日韩欧美在线看| 一区二区三区欧美在线| 欧美本精品男人aⅴ天堂| 亚洲免费在线精品一区| 欧美人与性动交cc0o| 亚洲第一黄网| 久久精品官网| 亚洲午夜视频| 欧美日韩精品中文字幕| 亚洲国产综合91精品麻豆| 久久久国际精品| 亚洲特色特黄| 欧美三级乱人伦电影| 日韩一级免费| 亚洲福利视频一区| 久久琪琪电影院| 狠狠色丁香婷综合久久| 久久国产福利国产秒拍| 一区二区三区视频免费在线观看| 欧美mv日韩mv亚洲| 亚洲高清视频一区| 久热爱精品视频线路一| 欧美一区日韩一区| 国产美女高潮久久白浆| 亚洲影视九九影院在线观看| 亚洲日本欧美日韩高观看| 欧美a级片一区| 亚洲激情中文1区| 欧美大片在线观看| 久久综合五月天婷婷伊人| 激情成人av在线| 久久一区亚洲| 久久亚洲国产精品日日av夜夜| 精品91久久久久| 美日韩在线观看| 毛片一区二区|