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还是有很多干货的。