工作时使用了Websocket技术,在使用的过程中发现,浏览器(Chrome)升级后可能会导致Websocket不可用,更换浏览器后可以正常使用。

近日偶尔一次在本地调试,发现使用相同版本的Chrome浏览器,不可连接线上服务器的WS服务,但是可以连接本地的WS服务。 此时初步怀疑是服务器在某种特殊情况下会触发无法连接的问题。

使用Wireshark抓包

Filter:    ip.dst==serverIP or (ip.dst==本地IP and ip.src==serverIP)

一.查看可以正常连接线上服务的浏览器的网络请求(搜狗高速核)

可以看到WebSocket连接建立的步骤:

1、先建立TCP连接,1~3条为tcp连接的三次握手

2、发出一个http请求,Header内容如下

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

websocket连接http请求header内容

服务器返回

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: LDedYtTLpS6J7EygF4awEchi+D4=

websocket连接http请求返回

3.连接建立成功,使用WebSocket协议收发消息

其中[FIN][MASKED] 为浏览器给server发消息

[FIN]为server给浏览器发消息,浏览器收到后发一个TCP的 [ACK] 包确认

正常的网络请求知道了,接下来服务器不变,更换浏览器

二、查看无法正常连接线上服务器的网络请求(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

和搜狗的包对比,好像没有任何问题,接下来浏览器不变,更换服务器

三、查看可以正常连接的本地服务器的网络请求(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级别的,所以重启浏览器后再用域名访问是可以的

第一、三个问题得到了解释

但是,搜狗浏览器的请求里边也包含了Cookie,为何搜狗却可以正常连接服务器呢?

搜狗高速核

用搜狗高速核多次试验,发现:

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) >= )
);
}

在正常情况下此方法返回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没有被干掉

使用wireshark抓包分析浏览器无法建立WebSocket连接的问题(server为Alchemy WebSockets组件)的更多相关文章

  1. Wireshark抓包分析TCP建立/释放链接的过程以及状态变迁分析

    Wireshark抓包分析TCP建立/释放链接的过程以及状态变迁分析 一.介绍计算机网络体系结构 1.计算机的网络体系结构 在抓包分析TCP建立链接之前首先了解下计算机的网络通信的模型,我相信学习过计 ...

  2. wireshark 抓包分析 TCPIP协议的握手

    wireshark 抓包分析 TCPIP协议的握手 原网址:http://www.cnblogs.com/TankXiao/archive/2012/10/10/2711777.html 之前写过一篇 ...

  3. FTP协议的粗浅学习--利用wireshark抓包分析相关tcp连接

    一.为什么写这个 昨天遇到个ftp相关的问题,关于ftp匿名访问的.花费了大量的脑细胞后,终于搞定了服务端的配置,现在客户端可以像下图一样,直接在浏览器输入url,即可直接访问. 期间不会弹出输入用户 ...

  4. wireshark抓包分析——TCP/IP协议

    本文来自网易云社区 当我们需要跟踪网络有关的信息时,经常会说"抓包".这里抓包究竟是什么?抓到的包又能分析出什么?在本文中以TCP/IP协议为例,简单介绍TCP/IP协议以及如何通 ...

  5. http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送

    使用 nginx 搭建一个 http2 的站点,准备所需: 1,域名 .com .net 均可(国内域名需要 icp 备案) 2,云主机一个,可以自由的安装配置软件的服务器 3,https 证书 ht ...

  6. Wireshark抓包分析/TCP/Http/Https及代理IP的识别

    前言 坦白讲,没想好怎样的开头.辗转三年过去了.一切已经变化了许多,一切似乎从没有改变. 前段时间调研了一次代理相关的知识,简单整理一下分享之.如有错误,欢迎指正. 涉及 Proxy IP应用 原理/ ...

  7. Wireshark抓包分析HTTPS与HTTP报文的差异

    一.什么是HTTPS: HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议 它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换 ...

  8. 使用wireshark抓包分析-抓包实用技巧

    目录 使用wireshark抓包分析-抓包实用技巧 前言 自定义捕获条件 输入配置 输出配置 命令行抓包 抓取多个接口 抓包分析 批量分析 合并包 结论 参考文献 使用wireshark抓包分析-抓包 ...

  9. WireShark抓包分析以及对TCP/IP三次握手与四次挥手的分析

    WireShark抓包分析TCP/IP三次握手与四次挥手 Wireshark介绍: Wireshark(前称Ethereal)是一个网络封包分析软件.功能十分强大,是一个可以在多个操作系统平台上的开源 ...

随机推荐

  1. es6转es5

    一.在没有IDE情况下的转换 在"我的电脑->D盘”新建个文件夹es6,文件夹里新建一个文件es6.js. 打开命令行窗口 1.先全局安装babel-cli,输入命令 npm inst ...

  2. CSS Reset

    html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, ac ...

  3. 串口计时工具Grabserial简介及修改(添加输入功能)

    Grabserial是Tim Bird用python写的一个抓取串口的工具,这个工具能够为收到的每一行信息添加上时间戳. 如果想对启动时间进行优化的话,使用这个工具就可以简单地从串口输出分析出耗时. ...

  4. Java中堆内存和栈内存详解

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  5. PHP基础知识之对象复制

    对象的复制默认为浅复制 进行深复制的方法为:在类中定义魔法方法__clone(),类的对象复制时,会自动调用 __clone方法,在 __clone方法中可以进行各种复制对象的个性化 class My ...

  6. Code First开发系列之管理并发和事务

    返回<8天掌握EF的Code First开发>总目录 本篇目录 理解并发 理解积极并发 理解消极并发 使用EF实现积极并发 EF的默认并发 设计处理字段级别的并发应用 实现RowVersi ...

  7. ENode框架Conference案例分析系列之 - ENode框架初始化

    前言 Conference案例是使用ENode框架来开发的.之前我没有介绍过ENode框架是如何启动的,以及启动时要注意的一些点,估计很多人对ENode框架的初始化这一块感觉很复杂,一头雾水.所以,本 ...

  8. Mina入门教程(二)----Spring4 集成Mina

    在spring和mina集成的时候,要十分注意一个问题:版本. 这是一个非常严重的问题,mina官网的demo没错,网上很多网友总结的代码也是对的,但是很多人将mina集成到spring中的时候,总是 ...

  9. JavaScript多文件下载

    对于文件的下载,可以说是一个十分常见的话题,前端的很多项目中都会有这样的需求,比如 highChart 统计图的导出,在线图片编辑中的图片保存,在线代码编辑的代码导出等等.而很多时候,我们只给了一个链 ...

  10. [.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境

    [.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境 本篇导读: 前面介绍了两款代码管理工具 ...