工做時使用了Websocket技術,在使用的過程當中發現,瀏覽器(Chrome)升級後可能會致使Websocket不可用,更換瀏覽器後能夠正常使用。html
近日偶爾一次在本地調試,發現使用相同版本的Chrome瀏覽器,不可鏈接線上服務器的WS服務,可是能夠鏈接本地的WS服務。 此時初步懷疑是服務器在某種特殊狀況下會觸發沒法鏈接的問題。nginx
使用Wireshark抓包web
Filter: ip.dst==serverIP or (ip.dst==本地IP and ip.src==serverIP)chrome
一.查看能夠正常鏈接線上服務的瀏覽器的網絡請求(搜狗高速核)跨域
能夠看到WebSocket鏈接創建的步驟:瀏覽器
一、先創建TCP鏈接,1~3條爲tcp鏈接的三次握手服務器
二、發出一個http請求,Header內容以下websocket
GET /write?agentId=255 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 10.134.71.235:2015
Origin: http://10.134.71.235
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: NddL4PEqgeUKIon0p+IHwQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0
Cookie: ASP.NET_SessionId=kdparak1ecjplo4erozul2yl; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=70467311a7ed8f62b58f8f1d65cdb408
服務器返回 cookie
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: LDedYtTLpS6J7EygF4awEchi+D4=
3.鏈接創建成功,使用WebSocket協議收發消息網絡
其中[FIN][MASKED] 爲瀏覽器給server發消息
[FIN]爲server給瀏覽器發消息,瀏覽器收到後發一個TCP的 [ACK] 包確認
正常的網絡請求知道了,接下來服務器不變,更換瀏覽器
2、查看沒法正常鏈接線上服務器的網絡請求(Chrome)
能夠看到,HTTP請求發出去後,沒有收到101的回覆,服務器直接發起了TCP鏈接斷開的流程。
懷疑是HTTP請求的內容不對。查看請求header
GET /write?agentId=255 HTTP/1.1
Host: 10.134.71.235:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://10.134.71.235
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=1rfwnghibfq2jvvjrlbflvl0; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=IsQky+6I5U5zM89pna9UNNirBD9v74G5799FdJvrK78aLTw0mvq5icQJpNlCweeHTl646j88InE03ayWm4PpcA==
Sec-WebSocket-Key: kcEwLRS2BowYzsoYxGCQNw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
和搜狗的包對比,好像沒有任何問題,接下來瀏覽器不變,更換服務器
3、查看能夠正常鏈接的本地服務器的網絡請求(Chrome)
鏈接創建過程正常,就不用截圖了,主要關注HTTP請求header裏邊的內容
GET /write?agentId=255 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin:http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Sec-WebSocket-Key: ldwAY7BvJ6c0Gt9Xbh/R/Q==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
分析緣由
分析發現,這個header裏邊沒有cookie的數據,推測有多是http請求裏邊的cookie致使鏈接斷開。
chrome
這裏就發現了第一個奇怪的問題,Chrome爲何有的時候會發送cookie,有的時候不會呢?
通過屢次調試發現:
1.關閉瀏覽器並重啓的第一次,使用m.venus.sogou-inc.com訪問,是能夠正常使用的
2.不管什麼時候,經過IP直接訪問服務器都沒法鏈接
3.只要用IP訪問過,用域名訪問也沒法正常鏈接了
分析Cookie
用域名訪問時的Cookie
用IP訪問時的Cookie
公司對m.venus.sogou-inc.com的解析是通過了代理服務器的,目前OP的nginx還不能轉發ws協議的請求
因此瀏覽器建立Websocket的方式 是 new WebSocket('ws://ip:port') 而不是 new WebSocket('ws://hostname:port')
重啓瀏覽器,用域名訪問的時候,header中
Host: 10.134.71.235:2015
Origin: http://m.venus.sogou-inc.com
這是一個跨域的http請求,因此請求中默認是不會帶上cookie的,chrome對在ws協議中的http請求中,顯然也是應用的這個默認設置。
用IP訪問的時候
Host: 10.134.71.235:2015
Origin: http://10.134.71.235
這是一個同域的請求,因此會帶上cookie。 第二個問題獲得瞭解釋
一旦用ip訪問過,在此ip下就種下cookie了,而即便是在域名訪問的狀況下,ws協議發出的http請求的host也是ip:port,因此也會發送ip下的cookie。
能夠看到cookies的生命週期都是session級別的,因此重啓瀏覽器後再用域名訪問是能夠的
第1、三個問題獲得瞭解釋
搜狗高速核
用搜狗高速核屢次試驗,發現:
1.搜狗高速核的cookie數據中沒有發送sessionid
2.搜狗瀏覽器無論任什麼時候候,都會發送cookie
用域名訪問的cookie:
用IP訪問的cookie:
經過對比cookie的值發現,發送的cookie是ip域下的cookie。
那麼這裏有兩個沒辦法解釋的問題
1.爲何帶有httponly屬性的cookie,搜狗瀏覽器在發送請求的時候不會發出去
2.爲什麼關掉全部搜狗瀏覽器的進程後,生命週期爲session的cookie沒有被幹掉
這是否是搜狗瀏覽器的兩個BUG呢?
到此時基本能夠肯定,就是由於cookie中帶了sessionid致使服務器主動斷開鏈接。
雲平臺的Websocket服務器是用Alchemy Websockets開發的。按照正常的邏輯,WebSocket服務器中不該該用session去判斷用戶身份,由於它和Http不屬於同一個會話。
如今懷疑是由於Alchemy WebSockets組件的BUG致使此問題。
調試Alchemy WebSockets源碼
首先在本地IIS中發佈一個雲平臺服務器,而後在VS中開啓一個調試的服務,經過IP登陸一下本地的雲平臺,而後經過localhost:調試端口 訪問調試的服務,以模擬ws鏈接的http請求中帶上cookie的狀況。
從鏈接開始階段打上斷點單步調試,到Handshake.cs類的時候發現一個方法:
public bool IsValid() { return ( (Host != null) && (Key != null) && (Int32.Parse(Version) >= 8) ); }
在正常狀況下此方法返回true,請求中帶上cookie且含有sessionId後返回false。緣由是key==null。
抓包獲得的header:
GET /write?agentId=261 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=5qmgzihkxtvntxirmekbm2tv; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=RGPCwqEawjg9JXHVZ/rLzM4Ac1+nDHlL2y1kKYp6PVLkZ5o/Oj5/OsP8t8vsg3D+djE3x0Q7zH/7ggw2Jme63A==
Sec-WebSocket-Key: TLjHvbrhmKqEK3sNPu7bnA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_windo
能夠看到Sec-Websocket-Key 是存在的。
調試進入分析處理header的類裏邊,發現收到的header字符串爲
GET /write?agentId=261 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=5qmgzihkxtvntxirmekbm2tv; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=
發現header少了一段。繼續分析這個組件的代碼,發現
Handler.cs 中:
TCPServer中:
默認只讀取了512字節的數據,把這裏改成一個足夠大的大小來測試一下,就沒問題了。
至此問題的緣由找到了,Alchemy WebSockets在處理ws鏈接第二步——發送http請求的時候,對http頭的解析方法有問題,致使丟掉了關鍵性的數據,瀏覽器中生成的 Ses-Websocket-Key,以致於服務器認爲這是個非法的鏈接請求,給幹掉了。
將處理header的地方修改成讀取全部數據再處理,就能解決這個問題。
可是調試過程當中仍是留下了兩個疑問:
1.爲何帶有httponly屬性的cookie,搜狗瀏覽器在發送請求的時候不會發出去
2.爲什麼關掉全部搜狗瀏覽器的進程後,生命週期爲session的cookie沒有被幹掉