工做时使用了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没有被干掉