2015年11月12日 星期四

[iOS XMPP] 關於XMPP Framework的大小事 - xmpp stream設定

上一篇我們已經提到了XMPP Framework裡面主要分為兩個部分,xmpp core & xmpp extension。而xmpp core裡面最重要的就是xmpp stream,所以今天我們來看看如何在我們的app裡設定xmpp stream吧!
(在設定以下這些東西,通常都是在AppDelegate中進行實作)

1. 首先,設定連線

其實設定連線步驟很簡單,只要設定JID就可以了,範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"tungtungtsai@gmail.com"];
至於剩下的就交給xmpp stream完成吧!等等..我都還沒跟xmpp stream說我的server在哪啊?沒關係,在這裡,當你輸入了XMPPJID,xmpp stream就會去做SRV查找_xmpp-client._tcp.domain。例如,我在範例中打的domain是gmail.com,google server就很可能回傳像是"talk.google.com"的東西,然後xmpp stream就可以去和這個server建立連線。當然,如果說SRV查找失敗的話,那麼xmpp stream就直接去連JID的domain了。所以你要是已經知道你準備要連過去的xmpp server並沒有xmpp SRV records,你可以直接告訴xmpp stream你的host name,讓他不用花時間去查找了(反正也找不到...),範例如下:
xmppStream.myJID = [XMPPJID jidWithString:@"user@tungtungtsai.com"];
xmppStream.hostName = @"tungtungtsai.com";
你直接指派hostname有個最大好處就是他在development xmpp server也可以work。例如你的伺服器沒有DNS address只有實體IP,或是只能在local network run,那也沒關係,一樣能夠work。範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"user@dev1.myCompany.com"];
xmppStream.hostName = @"192.168.2.27";
設定了這些之後還有一件事,就是port。我們可以選擇性的看要不要給port,對每個xmpp spec來說,預設xmpp server都是開port 5222,當然如果你要開不同port就要特別設定一下hostPost了。


2. 添加代理(Delegates)

上一篇提到關於XMPPStream有些讓framework更具彈性、延展性、易開發的設計,而其一就是MulticastDelegate。由於xmpp framework需要支援很多的extensions,包含官方的和個人開發者所自己撰寫的plugin,所以傳統的代理模式(Delegate pattern)並不適合。因此,MulticastDelegate就可以讓你在你自己的extension中使用標準的delegate paradigm,並且允許多個classes去接收同一個delegate notification。這樣地好處是你不需要把你所有的xmpp handling code放進單一個class。

這裡簡單提一下:

在iOS開發中,主要有兩個callback systems,delegate和notification。Delegate很直覺也很簡單。使用者把自己設為delegate,然後就可以實作自己需要的delegate method。例如:

[worker setDelegate:self];

- (void)workerDidFinish:(Worker *)sender
{
}

- (void)worker:(Worker *)sender didFinishSubTask:(id)subtask inDuration:(NSTimeInterval)elapsed
{
}

- (BOOL)worker:(Worker *)sender shouldPerformSubTask:(id)subtask
{
}
Notifications也不難,使用者需要個別地註冊每個notification type (寫起來複雜一些),例如:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinish:)
                                             name:WorkerDidFinishNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinishSubTask:)
                                             name:WorkerDidFinishSubTaskNotification
                                           object:nil];

- (void)workerDidFinish:(NSNotification *)notification
{
    Worker *sender = [notification object];
}

- (void)workerDidFinishSubTask:(NSNotification *)notification
{
    Worker *sender = [notification object];
    id subtask = [[notification userInfo] objectForKey:@"subtask"];
    NSTimeInterval elapsed = [[[notification userInfo] objectForKey"duration"] doubleValue];
}

有時候要從notification中的Dictionary取出參數的話,就要先知道他擁有哪些keys。另外,notification是不允許回傳變數的。因此簡單地對兩個callback機制的優缺點分析如下:

Delegate優點:

1. 易於註冊多個callback
2. 當有多個參數時較易於使用
3. 允許回傳變數

Delegate缺點:

1. 只能夠是單一delegate

Notification優點:

1. 可以多個物件註冊同一個notification

Notification缺點:

1. 註冊多個callback較複雜
2. 要從dictionary中拿到參數很麻煩(要知道正確的key)
3. 當需要回傳變數時就不能使用

所以,對於XMPP framework來說哪一個方式適用呢?對XMPP來說,xmpp framework需要能夠廣播給多個listeners。例如當你有個message進來,你需要同時廣播給聊天的視窗、歷史紀錄和推播系統等等。此外,xmpp framework需要易於延展。xmpp要能夠支持很多的XEP,所以要能在broadcast端與listener端易於使用。xmpp framework 需要選擇能支持回傳變數的方式,也需要能夠幫助維持 thread-safety,因為xmpp framework大量地使用平行化與多線程。

講完你就發現好像delegate和notification都不是最好的解決方法,因此xmpp就創了一個GCDMulticastDelegate class。你可以在任何時候把你自己新增/移除為XMPPStream的delegate。

[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
...
[xmppStream removeDelegate:self];
簡單範例如下:
// Add myself as a delegate, and tell xmppStream to invoke my delegate methods on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// Then just implement whatever delegate methods you need like normal
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
   ...
}
就和傳統的代理模式差不多,但是你可以具體地說明thread-specific資訊。而如果你想要讓某些process離開主線程到背景執行也很容易,在iPhone中這種操作方式可以讓你的app維持良好的perfomance,範例如下
// Handle most stuff on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// But do that one slow thing on a background queue so it doesn't slow down the UI anymore
[xmppStream addDelegate:bgProcessor delegateQueue:bgProcessorQueue];


3. 添加模組(Modules)

接下來就是要加入我們自己需要的extensions了,當然你也可以自己寫自己需要的extensions,這裡我們不會探討到底有哪些extensions,僅簡單列出一些,因為這裡最重要的是要學會加入模組。
XMPPReconnect - 當斷線時自動重新連線
XMPPRoster - 提供聯絡人功能
XMPPRoom - 提供聊天室,群聊功能
XMPPPubSub - 提供發布與訂閱功能
現在我們來試著加入XMPPReconnect module到我們的xmpp stream吧!

xmppReconnect = [ [XMPPReconnect alloc] init];

// Optional configuration of xmppReconnect could go here.
// The defaults are fine for our purposes.

[xmppReconnect activate:xmppStream];

// You can also optionally add delegates to the module.

[xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];

// And that's all that is needed.
// The module will receive any delegate methods it needs automatically
// from the xmpp stream, and will continue to do its thing unless you deactivate it.

4. 建立連線

當我們都加好要用的modules,就可以開始建立連線了!

NSError *error = nil;
if (![xmppStream connect:&error])
{
    NSLog(@"Oops, I probably forgot something: %@", error);
}

若是我們在過程中忘記設定必須的屬性,例如JID,那連線就會回傳NO,並在error message中給予提示。

在連線過程中,client端和server端會建立xmpp handshake(交握),達成協定。如果你的伺服器需要建立安全連線,例如透過SSL/TLS方式,xmpp stream會自動建立安全連線。如果你的伺服器有不正確的X509 certificate,那就必須去實作xmppStream:willSecureWithSettings: 這個方法來改掉預設的安全設定。

5. 認證(Authenticating)

在所有連線handshaking都已經完成後,就會開始呼叫 xmppStreamDidConnect:方法,並開始進行認證程序。程式碼如下:

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    [xmppStream authenticateWithPassword:password error:NULL];
}


五個步驟進行完,在實際執行時就能與xmpp stream進行連線操作嘍!


參考連結:https://github.com/robbiehanson/XMPPFramework/wiki/IntroToFramework

沒有留言:

張貼留言