在之前一篇文章裡嘗試了跨浏覽器的數據共享,最後提到使用LocalConnection還可以實現跨浏覽器消息交互的可行性。
花了兩個晚上簡略的研究了下,LocalConnection的單向通信非常的簡單,不過要實現多個終端交互,必須自己實現一套消息機制,見智見仁了。
為了簡單演示,本例使用了基於廣播的觀察者模式:每個終端可以訂閱自己感興趣的主題,也可以向廣播發送消息,通知其他感興趣的終端。
Demo: http://www.etherdream.com/FunnyScript/WebIPC/ (多開幾個浏覽器頁面小窗口,即可測試)
比較遺憾的是最新版的Chrome浏覽器仍然無法和其他浏覽器進程交互:(
如果沒有錯誤發生,應該就是如下的效果:
在任何一個頁面上的操作,都會立即同步到其他頁面裡,只要Observe了感興趣的主題。
為什麼要使用觀察者模式呢?因為跨進程的通信是比較耗資源的,所以不感興趣的消息可以直接不訂閱,而不是收到再放棄。
LocalConnection是為單一的通信設計的,雖然使用很簡單,但可用的接口少之又少。想直接用它來廣播事件,或者消息路由,門都沒有。
因此底層的消息發送上沒有太多可選余地,只能簡單的點對點發送。我們必須創建多個LocalConnection,來實現消息的匯聚和分發。
LocalConnection真正能用的只有兩個方法:
connect(name) —— 創建管道(每個LocalConnection只能創建一個管道,每個管道名只能有一個)
send(name, ...) —— 向管道發送數據
如果只有兩個終端通信,那麼一切都是那麼簡單。。。
只需簡單的將消息發送給對方即可。
不過有多個終端情況就大不相同了。由於我們是本地進程間通信,並沒有第三方服務器主持,加上LocalConnection只能點對點的發送消息。意味著每次廣播都要給其他所有的終端都發送一次,這樣復雜度就大大增加了。
為了簡化結構,我們模擬一個LocalConnection作為Host,在第一次啟動時運行。其余的作為Client,每次廣播消息都提交給Host,由它來調度。
Host維護著一個回調列表。當Client對某個主題(subject)感興趣時,可以發送<主題ID,自己的管道名>給Host來訂閱。於是Host就把此Client的管道名添加到該主題的回調列表裡。以後若有該主題的消息,即可根據回調列表通知訂閱的Client。
為了能讓Host和Client通信更簡單,這裡使用channel+ID的命名規則,來創建管道名。
Host的ID為空,於是Client發送數據只需send(channel)即可;
Client的ID從1~100,選一個沒用被占用的作為管道名。Host回調時只需send(channel+id)就能通知對應的Client。
然而,這個Host服務僅僅是假象的。我們根本沒法在頁面之外運行一個第三方服務,一切只能在頁面中實現!於是我們把第一次啟動的頁面作為Host。當這個頁面關閉時,我們再通知第二個頁面創建Host,以此類推。。。
由於沒有第三方服務器,每個Client都可以兼職做Host。到這裡,你是不是想到了局域網游戲?由於沒有服務器,第一個創建的玩家便是主機。當他退出時,主機就交給了第二個玩家。如果他沒按正常步驟,強制退出了游戲,那就很有可以造成主機丟失,數據沒來得及轉移給下個玩家,導致游戲斷線結束。
同樣,當我們Host所在頁面關閉時,會向所有Client發送一條退出消息。至於誰繼承王位,不用關心,誰先得知誰做~~ 唯一值得注意的就是:很多浏覽器不能正常觸發window.unload事件,這意外著Host可能還沒來得把回調列表移交給他的繼承者就已匆匆離去,於是後人就無法接管了。為了不讓這種情況出現,每當新的Host上任,就向所有的Client發送一個請求,讓大家把各自關注的主題重新發送一遍(之前關注的都保存著,就為了這個時候用)。因此,即使新上任的Host一無所有,大家也會把現狀告訴他,可立即投入工作中。
若是強制關閉了Host所在的頁面進程,那麼主機丟失後一切都將會掛起。這時所有Client發送的數據都將有去無回,只有等到之後出現數據發送失敗,才得知Host已經掛了。這時誰先發現這個錯誤,誰就接管Host工作吧。
當然,還可以考慮加上心跳機制,即使Host沒有掛掉,但其所在進程長時間占用CPU,導致LocalConnection無法響應消息事件,也可以考慮轉移Host了。
由於時間限制,本例還有不少BUG,以後再慢慢完善。
想看代碼可以浏覽:http://code.google.com/p/webipc/source/browse/
事實上純粹的本地通信意義並不大,只有配合遠程服務進行消息的交互,才更有意義。例如用戶開了多個微博頁面,傳統的模型必須為每個頁面發起一個長連接,來保持實時的數據接收。如果使用跨浏覽器通信,那麼只需讓Host發起一個連接即可,其余的Client訂閱自己想要的主題,最終只需一個連接就可以。