WebSocket是HTML5的一個重要特性,能提供client和server的雙向通訊,是很多WebAPP做網路通訊的首選,不過它在Android的WebKit中並不支援,也就造就了socket.io的流行。在我們雲OS中當然不能漏掉這個特性,我這周的任務就是要在雲OS的Webkit中支援websocket,以此讓WebApp能直接調用websocket API。
某些平台的webkit(比如Android)不支援websocket並不是webkit沒管這塊,而是這一特性需要在各個平台特有的網路層中做porting。所以初步判斷只需要補全網路層的讀寫即可。先看看源碼WebCore/Modules/websockets,和websocket相關的代碼都在這個檔案夾中,其結構如下:
通過WebSocket.cpp, WebSocket.h和WebSocket.idl封裝websocket js API,所以在js中new WebSocket("...",...");時,反映到webkit就是:
WebSocket.cpp -> WebSocketChannel.cpp -> WebSocketFrame.cpp, WebSocketHandshake.cpp -> SocketStreamHandle
其中,WebSocket.cpp是JS API封裝,WebSocketChannel.cpp是channel的封裝,WebSocketFrame.cpp和WebSocketHandshake.cpp處理websocket的上層協議,SocketStreamHandle處理網路流。當SocketStreamHandle讀到資料後,又通過:
SocketStreamHandle->SocketStreamHandleClient->WebSocketChannelClient
這樣的路徑把訊息發送到JS中的onmessage的回調中,其中WebSocketChannel.cpp是SocketStreamHandleClient的實現,WebSocket.cpp是WebSocketChannelClient的實現,因此這麼看來WebSocket整個下層的結構和資訊迴路就清楚了。
分析完webkit代碼後,會發現webkit已經把websocket的架子和協議都已做好,唯獨缺了SocketStreamHandle中資料的讀和寫,也證實了我開始的判斷。看SocketStreamHandleBase.h和SocketStreamHandleBase.cpp會發現,只需要繼承SocketStreamHandleBase並實現虛方法platformSend和platformClose,另外讀到資料後調用SocketStreamHandleClient.didReceiveSocketStreamData把資料發送上層,這樣整個websocket就完整了。
實現這幾個方法並不困難,需要關注的是這裡需要基於非同步網路讀寫,功底好的可以直接epoll或者select,如果新手或者有跨平台的需求,可以用libevent,我就是使用了這個庫,具體代碼就不貼了,標準libevent讀和寫足矣。
補全websocket後,不但可以在JS API中提供websocket支援,也可以在native端提供websocket client端的能力,和js不同的是,native沒有js的執行環境,也不需要WebSocket.cpp的封裝,所以應該直接從WebSocketChannel.cpp開始用起,比如:
RefPtr<WebSocketChannel> wsChannel = WebSocketChannel::create(page->mainFrame()->document(), channel); KURL url = KURL(KURL(), String("ws://127.0.0.1:9223")); wsChannel->connect(url, String("webkit"));
這裡的channel是WebSocketChannelClient的一個實現,在js環境是這個實現是WebSocket.cpp,而在native端就需要我們單獨實現,重要的是實現didReceiveMessage方法,這是websocket協議解析後的message回調,這樣就相當於在js中使用onmessage方法了。
其他websocket的部分就不多提了,對協議有興趣的朋友可以重點看WebSocketFrame.cpp和WebSocketHandshake.cpp,對webkit idl感興趣的可以看WebSocket.cpp, WebSocket.h和WebSocket.idl。總之,小小的websocket還是有很多乾貨的。