当前位置: 代码迷 >> Web前端 >> 新浪微博Web版中的私信聊天效能的几个技术细节
  详细解决方案

新浪微博Web版中的私信聊天效能的几个技术细节

热度:106   发布时间:2013-02-25 10:23:36.0
新浪微博Web版中的私信聊天功能的几个技术细节

                                                                                        新浪微博Web版中的私信聊天功能的几个技术细节

蒋彪@南京

1.    Pull而不是push

一般在网页上要取得实时聊天数据,无外乎两种技术实现

a.      用ajax定时发起轮训, 主动的pull服务器端数据

b.     基于Flash,ActiveX,Html5等富客户端技术, 和server建立长连接,server端实时的push到客户端

新浪的实现是长轮训, 观察它的http请求,每隔一段时间网页就会发起如下的一个GET request(时间间隔不是固定的,怀疑server端做了指数推移)

(基于iframe的流pull?)

http://4.136.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback313&message=%5B%7B%22channel%22%3A%22%2Fmeta%2Fconnect%22%2C%22connectionType%22%3A%22callback-polling%22%2C%22id%22%3A271%2C%22clientId%22%3A%22gt2psmdubshz54tl6j%22%7D%5D&1361434136747

将参数unescape之后,这个url就是

http://4.136.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback313&message=[{"channel":"/meta/connect","connectionType":"callback-polling","id":271,"clientId":"gt2psmdubshz54tl6j"}]&1361434136747

 

请求的头文件Connection设置为keep-alive,使用http1.1中的长连接,并且服务器端在没有新消息的情况下不返回response,造成事实上的长连接。

      如果server端没有收到任何新消息,时间到之后,返回如下response

try{parent.org.cometd.script._callback44([{"id":"46","successful":true,"channel":"/meta/connect"}])}catch (ex) {}

                         浏览器收到之后,再次发起一次请求,如此轮训

            如果server端收到新消息之后,会立即返回如下格式的response

try{parent.org.cometd.script._callback28([{"id":"30","successful":true,"channel":"/meta/connect"},
{"data":{"type":"msg","items":[[1759237047,"ccc",1361497541084,"web_v4","3548440233536175"]]},"id":"c7y9s3","channel":"/im/j8vamp_1647676027"}])}
catch (ex) {}

这段response会被浏览器按js解析执行,将ccc这个message动态刷到聊天框中


2.    如何Send Message

同理,在聊天框中发送消息的话,也是用ajax发送如下get request。

http://0.136.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback44&message=%5B%7B%22channel%22%3A%22%2Fim%2Freq%22%2C%22data%22%3A%7B%22uid%22%3A%221759237047%22%2C%22seq%22%3A%221759237047%22%2C%22msg%22%3A%22testFromTony%22%2C%22cmd%22%3A%22msg%22%7D%2C%22id%22%3A46%2C%22clientId%22%3A%221uq7v70nfjl0whi0gd4%22%7D%5D&1361497717707

同样,转义之后,有意义的request如下

http://0.136.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback44&message=[{"channel":"/im/req","data":{"uid":"1759237047","seq":"1759237047","msg":"testFromTony","cmd":"msg"},"id":46,"clientId":"1uq7v70nfjl0whi0gd4"}]&1361497717707

其中的testFromTony就是message

server端成功接收到request之后的返回如下

try{parent.org.cometd.script._callback44([{"id":"46","successful":true,"channel":"/im/req"},
{"data":{"ret":0},"id":"46","channel":"/im/j8vamp_1647676027"}])}catch (ex) {}

3.    好友聊天的安全

新浪微博的聊天机制依赖于Cookie和ServerTokenID双重保证。

你可以登录新浪微博之后,在浏览器中打开另一个tab,跑以下的html。下面的页面因为和新浪微博共享内存cookie,因此可以自动发message

<html>
<head>
</head>
<body>
<img src='http://0.136.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback44&message=[{"channel":"/im/req",
           "data":{"uid":"1759237046","seq":"1759237046","msg":"testFromTonyaaaadddd1","cmd":"msg"},
           "id":46,"clientId":"1uw3tes1u5xjjv0vpou"}]1361502107092'></img>
</body>
</html>

关键在于发送的url中,有一个参数很重要,是clientId。这个ID是每次登录时,由服务器端分配出来的唯一标识客户端的ID,这个ID不正确,发送会出错。

更要命的是,不同的客户端login到微博上面的时候,服务器集群会把聊天信息定向到不同的聊天APIServer上去,比如

        http://1.126.web0.im.weibo.com

        http://0.136.web0.im.weibo.com

 

幸好,微博自己的页面里把这些数据当作Dom元素记下来了,其中就有clientId, API server domain等重要数据。如下:

<iframe id="cometd_uc" style="position: absolute; left: -100px; top: -100px; height: 1px; width: 1px; visibility: hidden; display: none;">
 <html>
 <head>
<script type="text/javascript" src="http://1.126.web0.im.weibo.com/im?jsonp=parent.org.cometd.script._callback16&message=%5B%7B%22channel%22%3A%22%2Fmeta%2Fconnect%22%2C%22connectionType%22%3A%22callback-polling%22%2C%22id%22%3A18%2C%22clientId%22%3A%223y0wflmlof3hccq3u5%22%7D%5D&1361512732973" charset="utf-8">
try{parent.org.cometd.script._callback16([{"id":"18","successful":true,"channel":"/meta/connect"}])}catch (ex) {} 
</script>
</head>
<body></body>
</html>
</iframe>

可惜的是,尝试用各种各样的XSS想拿到这个dom元素都失败了。微博在所有的输入框中都做了encode不知道有哪位大神能发现一个可以入侵的窗口。

 

版本归蒋彪所有, 转载请注明出自《南湖边上的小木屋》

#以上#



1楼guangboo昨天 14:53
应该是使用了类似BOSH的传输协议,可以在WEB上模拟TCP连接。只是服务器端要做一些处理,在连接超时前如果没有状态更新就不返回(response),否则返回空等信息。客户端收到返回再重新发送请求,这样就实现了类似于TCP的常连接效果。
Re: nanjingjiangbiao昨天 15:00
回复guangboon原来如此,受教受教。用长轮训模拟长连接,原来这个技术名叫BOSH。我孤陋寡闻了
  相关解决方案