最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實(shí)現(xiàn)同時(shí)兼容老版和新版Socket協(xié)議的一個(gè)簡(jiǎn)單WebSocket服務(wù)器

 更新時(shí)間:2014年06月04日 09:53:23   作者:  
這篇文章主要介紹了Python實(shí)現(xiàn)同時(shí)兼容老版和新版Socket協(xié)議的一個(gè)簡(jiǎn)單WebSocket服務(wù)器,需要的朋友可以參考下

最近在做的一個(gè)項(xiàng)目中需要使用到HTML5中引入的WebSocket技術(shù),本來(lái)以為應(yīng)該很容易就能搞定,誰(shuí)知道在真正上手開(kāi)發(fā)了以后才發(fā)現(xiàn)有很多麻煩的地方,雖然我們是一個(gè)以前端開(kāi)發(fā)和設(shè)計(jì)見(jiàn)長(zhǎng)的團(tuán)隊(duì),而且作為一個(gè)二手程序猿又長(zhǎng)期不被待見(jiàn),但是為了讓有同樣需求的朋友少走些彎路,我還是決定把實(shí)現(xiàn)方法貼在這個(gè)地方。

關(guān)于WebSocket的基本概念,維基百科上解釋的很清楚,而且網(wǎng)上也能搜出來(lái)一大把,這里就略過(guò)不表,直接進(jìn)入正題。

這次的問(wèn)題首先有一個(gè)前提,就是得用Python來(lái)實(shí)現(xiàn)這個(gè)服務(wù)器,如果對(duì)具體語(yǔ)言沒(méi)有限制的話,推薦大家首選Node.js的一個(gè)第三方庫(kù):Socket.IO,非常好用,10分鐘不打針不吃藥搞定WebSocket Server,而且用JS來(lái)寫(xiě)后端,相信也能對(duì)上很多文藝開(kāi)發(fā)者的胃口。

但是如果選擇用Python,google搜索的結(jié)果幾乎都不能用,最要命的問(wèn)題是,WebSocket協(xié)議本身還是一個(gè)草案,所以不同瀏覽器支持的協(xié)議版本有所不同,Safari 5.1支持的是老版本協(xié)議Hybi-02,Chrome 15以及Firefox 8.0支持的是新版本協(xié)議Hybi-10,老版本協(xié)議和新版本協(xié)議在建立通信的握手方法還有數(shù)據(jù)傳輸?shù)母袷揭笊隙加兴煌?,?dǎo)致網(wǎng)上大多數(shù)實(shí)現(xiàn)方式只能適用于Safari瀏覽器,并且Safari和C&F瀏覽器之間無(wú)法互相通信。

首先第一步需要解釋的是新、舊版本W(wǎng)ebSocket協(xié)議的握手方式。我們先來(lái)看看三個(gè)不同瀏覽器發(fā)送的握手?jǐn)?shù)據(jù)的結(jié)構(gòu):

Chrome:

復(fù)制代碼 代碼如下:

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:1337
Sec-WebSocket-Origin: http://127.0.0.1:8000
Sec-WebSocket-Key: erWJbDVAlYnHvHNulgrW8Q==
Sec-WebSocket-Version: 8
Cookie: csrftoken=xxxxxx; sessionid=xxxxx

Firefox:
復(fù)制代碼 代碼如下:
GET / HTTP/1.1
Host: 127.0.0.1:1337
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:8.0) Gecko/20100101 Firefox/8.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive, Upgrade
Sec-WebSocket-Version: 8
Sec-WebSocket-Origin: http://127.0.0.1:8000
Sec-WebSocket-Key: 1t3F81iAxNIZE2TxqWv+8A==
Cookie: xxx
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

Safari:
復(fù)制代碼 代碼如下:
GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: 127.0.0.1:1337
Origin: http://127.0.0.1:8000
Cookie: sessionid=xxxx; calView=day; dayCurrentDate=1314288000000
Sec-WebSocket-Key1: cV`p1* 42#7  ^9}_ 647  08{
Sec-WebSocket-Key2: O8 415 8x37R A8   4
;"######

 

可以看出,Chrome和Firefox實(shí)現(xiàn)的是新版協(xié)議,因此只傳輸了一個(gè)”Sec-WebSocket-Key”頭以供服務(wù)端生成握手Token,但是遵循老版本的Safari的數(shù)據(jù)中有兩個(gè)Key:”Sec-WebSocket-Key1″和”Sec-WebSocket-Key2″,因此服務(wù)端在生成握手Token的時(shí)候,需要做一次判斷。先來(lái)看使用老版本協(xié)議的Safari,Token生成算法如下:

取出Sec-WebSocket-Key1中的所有數(shù)字字符形成一個(gè)數(shù)值,這里是1427964708,然后除以Key1中的空格數(shù)目,這里好像是6個(gè)空格,得到一個(gè)數(shù)值,保留該數(shù)值整數(shù)位,得到數(shù)值N1;對(duì)Sec-WebSocket-Key2如法炮制,得到第二個(gè)整數(shù)N2;把N1和N2按照Big-Endian字符序列連接起來(lái),然后再與另外一個(gè)Key3連接,得到一個(gè)原始序列ser_key。那么Key3是什么呢?大家可以看到在Safari發(fā)送過(guò)來(lái)的握手請(qǐng)求最后,有一個(gè)8字節(jié)的奇怪的字符串“;”######”,這個(gè)就是Key3?;氐絪er_key,對(duì)這個(gè)原始序列做md5算出一個(gè)16字節(jié)長(zhǎng)的digest,這就是老版本協(xié)議需要的token,然后將這個(gè)token附在握手消息的最后發(fā)送回Client,即可完成握手。

新版協(xié)議生成Token的方法比較簡(jiǎn)單:首先把Sec-WebSocket-Key和一串固定的UUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”做拼接,然后對(duì)這個(gè)拼接后的字符串做SHA1加密,得到digest以后,做一次base64編碼,即可獲得Token。

另外需要注意的是,新版本和老版本握手協(xié)議回傳給Client的數(shù)據(jù)結(jié)構(gòu)有所不同,在附件中的server源碼中寫(xiě)得很清楚了,看看就能明白。
完成握手只是WebSocket Server的一半功能,現(xiàn)在只能保證這個(gè)Server能夠和兩個(gè)版本的瀏覽器建立鏈接了,但是如果試著把Chrome中的消息發(fā)送給Safari,會(huì)發(fā)現(xiàn)Safari無(wú)法接收。導(dǎo)致這個(gè)結(jié)果的原因,是因?yàn)閮蓚€(gè)版本的協(xié)議的Data Framing結(jié)構(gòu)不同,也即是在握手建立連接后,Client發(fā)送和接收的數(shù)據(jù)結(jié)構(gòu)都不一樣。

首先第一步需要獲取不同版本協(xié)議下Client發(fā)送過(guò)來(lái)的原始數(shù)據(jù)。老版本協(xié)議比較簡(jiǎn)單,實(shí)際上就是在原始數(shù)據(jù)前加了個(gè)'\x00′,在最后面加上了一個(gè)'\xFF',所以如果Safari的Client發(fā)送一個(gè)字符串'test',實(shí)際上WebSocket Server收到的數(shù)據(jù)是:'x00test\xFF',所以只需要?jiǎng)冸x掉首尾那兩個(gè)字符就可以了。

比較麻煩的是新版本協(xié)議的數(shù)據(jù),按照新版draft的解釋,Chrome和Firefox發(fā)過(guò)來(lái)的數(shù)據(jù)報(bào)文由以下幾個(gè)部分組成:首先是一個(gè)固定的字節(jié)(1000 0001或是1000 0002),這個(gè)字節(jié)可以不用理會(huì)。麻煩的是第二個(gè)字節(jié),這里假設(shè)第二個(gè)字節(jié)是1011 1100,首先這個(gè)字節(jié)的第一位肯定是1,表示這是一個(gè)”masked”位,剩下的7個(gè)0/1位能夠計(jì)算出一個(gè)數(shù)值,比如這里剩下的是 011 1100,計(jì)算出來(lái)就是60,這個(gè)值需要做如下判斷:

如果這個(gè)值介于0000 0000 和 0111 1101 (0 ~ 125) 之間,那么這個(gè)值就代表了實(shí)際數(shù)據(jù)的長(zhǎng)度;如果這個(gè)數(shù)值剛好等于0111 1110 (126),那么接下來(lái)的2個(gè)字節(jié)才代表真實(shí)數(shù)據(jù)長(zhǎng)度;如果這個(gè)數(shù)值剛好等于0111 1111 (127),那么接下來(lái)的8個(gè)字節(jié)代表數(shù)據(jù)長(zhǎng)度。

有了這個(gè)判斷之后,能夠知道代表數(shù)據(jù)長(zhǎng)度的字節(jié)在第幾位結(jié)束,比如我們舉得例子60,這個(gè)值介于0~125之間,所以第二個(gè)字節(jié)本身就代表了原始數(shù)據(jù)的長(zhǎng)度了(60個(gè)字節(jié)),所以從第三個(gè)字節(jié)開(kāi)始,我們能抓出4個(gè)字節(jié)來(lái),這一串字節(jié)叫做 “masks” (掩碼?),掩碼之后的數(shù)據(jù),就是實(shí)際的data…的兄弟了。說(shuō)是兄弟,是因?yàn)檫@個(gè)數(shù)據(jù)是實(shí)際data根據(jù)掩碼做過(guò)一次位運(yùn)算后得到的,獲得原始data的方法是,將兄弟數(shù)據(jù)的每一位x,和掩碼的第i%4位做xor運(yùn)算,其中i是x在兄弟數(shù)據(jù)中的索引??吹醚刍ㄊ前?,看看下面這個(gè)代碼片段也許就能明白了:

復(fù)制代碼 代碼如下:

def send_data(raw_str):
    back_str = []

    back_str.append('\x81')
    data_length = len(raw_str)

    if data_length < 125:
        back_str.append(chr(data_length))
    else:
        back_str.append(chr(126))
        back_str.append(chr(data_length >> 8))
        back_str.append(chr(data_length & 0xFF))

    back_str = "".join(back_str) + raw_str

這樣生成的back_str,就能夠發(fā)送給使用新版協(xié)議的Chrome或是Firefox了。

至此,這個(gè)簡(jiǎn)單的WebSocket Server就完成了,能夠同時(shí)兼容老版協(xié)議和新版協(xié)議的Socket連接,以及不同版本之間的數(shù)據(jù)傳輸。該Server的源碼請(qǐng)點(diǎn)擊這里下載,需要注意的是里面用到了twisted框架來(lái)跑TCP服務(wù),代碼寫(xiě)得不怎么樣,僅供大家參考。

相關(guān)文章

最新評(píng)論

黔南| 宁明县| 株洲县| 方山县| 嘉义市| 林芝县| 阳信县| 垫江县| 五原县| 哈尔滨市| 敦化市| 额敏县| 克什克腾旗| 荆门市| 密云县| 太仓市| 攀枝花市| 宜丰县| 东乡县| 龙口市| 松江区| 巴东县| 南木林县| 平谷区| 高碑店市| 大宁县| 吉木乃县| 特克斯县| 西贡区| 东平县| 横峰县| 嘉禾县| 交城县| 麻江县| 平顶山市| 潜山县| 高雄市| 砀山县| 邛崃市| 游戏| 绍兴县|