1. 白十三的码路首页
  2. 技术应用

如何解决WebSocket Server返回数据不一致

使WebSocket完美适配客户端

针对实时Web应用(如:实时通信、股票基金应用、体育实况更新、多玩家游戏等场景),传统Web中为了实时获取Server端的数据,通常是Client端定期发送HTTP请求,Server端进行响应并返回数据。由于Client定期向Server发送请求,当Server端没有数据更新时,Client仍旧发送请求,这造成了带宽的浪费以及Server端CPU的占用。为解决上述问题,越来越多企业在思考如何解决长连接问题,WebSocket是较为常用的方法之一。

WebSocket通过第一个HTTP request 建立 TCP 连接,后续数据交换都无需再发送 HTTP request,创建了一个真正的长连接。同时WebSocket 还是一个双通道的连接,可以实现在同一个TCP 连接上收发信息。

白山云聚合平台也融入了WebSocket,可以为用户提供WebSocket协议到HTTP协议的转换功能,让用户的Client以长连接WebSocket协议的方式连接到云聚合平台,云聚合只需一个HTTP连接即可连接到企业后端,大幅降低后端压力的同时,更免去了用户服务器端适配WebSocket协议的问题。我们在研发测试过程中遇到了一个有意思的问题,这或许是很多开发者都曾遇到过的:使用不同的WebSocket客户端和WebSocket Server通信,WebSocket Server返回数据不一致。

问题场景

  • 不同客户端访问

(1)python通过WebSocket客户端和WebSocket Server ws://2abe356fc.bsclink.com/交互,输出正常;

如何解决WebSocket Server返回数据不一致
python 客户端输出内容

(2)Chrome浏览器加载ws.html页面之后,页面中的js调用浏览器自带的WebSocket  Client与WebSocket Server ws://2abe356fc.bsclink.com/交互,输出ERROR;

如何解决WebSocket Server返回数据不一致
Chrome浏览器输出内容

(3)Safari浏览器加载ws.html页面之后,页面中的js调用浏览器自带的WebSocket Client和WebSocket Server ws://2abe356fc.bsclink.com/交互,输出正常;

如何解决WebSocket Server返回数据不一致
Safari浏览器输出内容
  • 浏览器请求流程

以下是浏览器通过WebSocket协议向服务器请求的流程:

如何解决WebSocket Server返回数据不一致
浏览器请求流程图

问题分析

只有Chrome与Websocket Server间的通信发生异常,判断ERROR很可能是由Chrome浏览器问题导致的,基于此来分析问题产生的具体原因。

  • 通过浏览器控制台查看报错相关信息
如何解决WebSocket Server返回数据不一致

如上图下方所示,WebSocket协议decode a text frame在转化为uft-8编码时失败。

由于WebSocket Server向Client返回数据时,使用text frame方式,于是我们开始排查WebSocket Server返回数据导致decode失败的原因。

  • 打印WebSocket Server日志,查看返回内容

通过日志,观察到longloop传送给WebSocket Server的内容与WebSocket Server输出到Client的内容一致,均为乱码。基于此我们可以确定WebSocket Server不存在异常情况,于是我们需要确定longloop是否存在异常。

  • 通过longloop抓包查看backend返回内容

可通过TCPDUMP抓包来判定longloop是否存在问题。

如何解决WebSocket Server返回数据不一致

通过对比以上两组数据,可以得出如下结论:

经过longloop后,真实返回给Client的数据并未发生变化。

(1)   backend的返回数据被gzip压缩;

(2)  压缩的响应数据被发送至WebSocket Server;

(3)  最终由WebSocket Server发送到WebSocket客户端。

  • backend返回的数据为什么被压缩了?

首先,backend端必须开启gzip压缩,并支持对此返回的数据类型的gzip压缩,才能返回压缩后的响应数据;

其次,客户端要明确声明能接收gzip压缩的响应数据,backend端才能够返回gzip压缩过的数据。

经确认,backend server上的配置开启了gzip压缩功能,并对content-type为text/html的数据支持gzip压缩。

可以判断问题有可能出现在Client环节:

(1)Client没有要求返回压缩数据,但是backend端返回了压缩数据;

通过不同浏览器访问,返回不同数据,可以判定不是backend端的问题。

(2)Client主动要求backend端返回被压缩的数据;

只有Chrome浏览器返回了gzip压缩数据,可以推断可能是因为Chrome请求backend端时,在request header中包含了可以接收gzip压缩数据的header,导致backend端返回了gzip压缩数据。

  • 抓包对比Chrome和Safari请求头信息

Chrome相关信息:

(1) Chrome浏览器请求ws.html静态文件的请求头中带有Accept-Encoding:

如何解决WebSocket Server返回数据不一致

(2)Chrome浏览器将ws.html加载到本地后,ws.html文件中的js WebSocket 客户端向WebSocket Server发送请求的请求头中带有Accept-Encoding:

如何解决WebSocket Server返回数据不一致

(3)Chrome浏览器的请求发送到longloop之后,longloop到backend的请求头中带有Accept-Encoding:

如何解决WebSocket Server返回数据不一致

Safari相关信息:

(1)Safari浏览器请求ws.html静态文件的请求头中带有Accept-Encoding:

(2)Safari浏览器将ws.html加载到本地后,ws.html文件中的js WebSocket 客户端向WebSocket Server发送请求的请求头中未带有Accept-Encoding:

如何解决WebSocket Server返回数据不一致
如何解决WebSocket Server返回数据不一致

(3)Safari浏览器的请求发送到longloop之后,longloop到backend的请求头中未带有Accept-Encoding:

如何解决WebSocket Server返回数据不一致

通过对比Chrome和Safari相关请求数据,我们可以判断出WebSocket Server返回数据不一致的原因如下:

Chrome、Safari浏览器发送请求时,为了提高网络传输效率、减少网络带宽占用,默认自带gzip压缩支持,两种浏览器加载ws.html时均无异常。但当js调用Chrome浏览器WebSocket客户端向WebSocket Server端发送请求时,在请求头Accept-Encoding中添加了对gzip的支持,backend收到HTTP请求后,认为客户端能够对gzip压缩的响应数据进行解压缩,从而backend返回了gzip压缩过的响应数据,而WebSocket客户端接收到gzip压缩的数据后,不支持gzip数据解压缩,最终导致了decode出错。

而js调用Safari浏览器WebSocket客户端向WebSocket Server端发送请求时,请求头未带有Accept-Encoding,backend收到HTTP请求后,不会返回被gzip压缩的响应数据,从而WebSocket客户端正常解析访问正常。

解决办法

为解决上述问题,我们需要在longloop这一层进行判断:如果user agent为Chrome浏览器,则需要去掉request header中的Accept-Encoding这个header,明确告知服务器端不接受gzip压缩过的数据,这样服务器端就不会返回gzip压缩过的数据,Chrome浏览器即可正常访问。

原创文章,作者:白山码路长,如若转载,请注明出处:http://blog.baishan.com/technology-applications/websocket-server-fanhuishujubuyizhi

发表评论

登录后才能评论