今天是2018年04月21日。

  过去的这一个多月里,我的工(开)作(发)任务转战回了游戏。短短的一个月里,催着输出两款h5游戏,再加上对接、联调,想想真是够辛(ku)苦(bi)的。本人负责后端,也就是服务端这块的游戏主流程输出。去年下半年,在前任大佬的带领下,做过一两款棋牌类的手游,虽然目前的运营状况不太乐观。不过好在,过去学的那点皮毛也还没丢光,所以这次写h5后端总体还算顺畅。至于怎么用Java来写游戏,下来如果有时间会整理下这块的思路和知识。

关于WebSocket,维基百科是这样介绍的:

   以前,很多网站为了实现实时推送技术,所用的技术都是轮询。轮询是在特定的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端。这种传统的模式带来的缺点很明显,即浏览器需要不断的向服务器发出请求,然而HTTP请求包含较多的请求头信息,而其中真正有效的数据只是很小的一部分,显然这样会浪费很多的带宽等资源。在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
   WebSocket是一种在单个TCP连接上进行全双工通讯的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

  WebSocket 协议在2008年诞生,2011年成为国际标准,现在几乎所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  简单来说,WebSocket减少了客户端与服务器端建立连接的次数,减轻了服务器资源的开销,只需要完成一次HTTP握手。整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直与客户端保持连接,直到双方发起关闭请求,同时由原本的客户端主动询问,转换为服务器有信息的时候推送。所以,它能做实时通信(聊天室、直播间等),其他特点还包括:

  • 建立在 TCP 协议之上,服务器端的实现比较容易
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
  • 数据格式比较轻量,性能开销小,通信高效
  • 可以发送文本,也可以发送二进制数据
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL

  差点就跑题了。这不,由于业务需求,上头要求新出的h5游戏要配上Https。无奈,公司小,没有专业的运维人员,所以只能由我们这些开发“猿”顶上了,以为会很顺畅,但一连串的问题没想到也才刚刚开始。因此本文,就是用来记录这些踩过的“坑”,希望可以让后人少走点弯路。

1. 申领证书

   公有云服务器上,一般大家都习惯使用Nginx来做反向代理。首先,配置Https,需要我们到专业的CA机构去申领证书,这个证书大多数情况下都是要钱的,但其实也有免费的(有效期1年),例如利用国内的阿里云或者腾讯云就可以很方便的申请这证书。

   - 阿里云 - Https证书申请
   - 腾讯云 - Https证书申请

  PS: 通过阿里云申领免费版SSL证书有点套路,藏得有点深。点击以上链接进入后,如果在“证书类型”一栏中没找到“免费型DV SSL”,那么请依次点击第三栏的“选择品牌”中的“Symantec”,然后回到第一栏的“证书类型”,点击出现的第三个选项“增强型OV SSL”,之后就会在“证书类型”中出现我们需要的第二项:“免费型DV SSL”。

腾讯云Https证书申请

  确认申领、购买之后,下来还需要绑定我们的域名(注意:免费型的SSL证书一般仅支持绑定一个一级域名或者子域名,通配符的证书一般是需要花钱的),以及进行域名身份验证等操作。等这两步都完成之后,只需要等待CA机构扫描认证之后,我们就可以拿到真正的证书了。

2. 配置Https

  下载好证书压缩包并解压之后,一般里面有IIS、Apache和Nginx三款主流服务器的ssl证书,这里我们也仅需要Nginx的证书。首先,将证书里Nginx文件夹下的1_{域名}bundle.crt 和2{域名}.key复制到我们服务器上的指定位置(假设在/root/ssl/下面)。基于Nginx的Https配置还是比较简单的,参考如下。

        server {#listen 80; #如果需要同时支持http和httpslisten 443 ssl http2; listen [::]:443 ssl http2; ssl_certificate "/root/ssl/1_{域名}_bundle.crt";ssl_certificate_key "/root/ssl/2_{域名}.key";ssl_session_cache shared:SSL:1m;ssl_session_timeout  10m;ssl_ciphers HIGH:!aNULL:!MD5;ssl_prefer_server_ciphers on;server_name {域名};location / {proxy_pass http://localhost:{代理端口};}}

  附:下面是开启Nginx的Gzip压缩的配置,有需要的也可以参考。

        http {gzip on;gzip_disable "msie6";gzip_min_length 1k;gzip_vary on;gzip_proxied any;gzip_comp_level 6;gzip_buffers 16 8k;gzip_http_version 1.1;gzip_types application/font-woff text/plain application/javascript application/json text/css application/xml text/javascript image/jpg image/jpeg image/png image/gif image/x-icon;server {# 这里是server相关的配置}}

3. 事故现场

  完成以上步骤后,按道理来说,h5游戏确实可以通过https的形式来打开了,简单测试后的确没啥问题,然后大家也就这样愉快的下班了。不过正如“墨菲定律”所说的:“凡事只要有可能出错,那就一定会出错”。果不其然,一段时间后,测试就在群里反馈,某段时间后h5游戏就无法加载正常进行下去了,一看时间,正是配完Https之后开始出现的问题。没办法,于是连忙打开电脑,开始排查解决问题,直觉告诉我要先打开浏览器的控制面板,果不其然,立刻发现了问题。

Mixed Content: The page at ‘https://{域名}.com/‘ was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint ‘ws://{ip}:{port}/‘. This request has been blocked; this endpoint must be available over WSS.
Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

  好家伙,这种情况,毫无疑问我们就需要使用 wss:// 安全协议了,于是立即联系h5客户端,把连接服务端webscoket的形式由ws:// 改为 wss:// 。本以为这样就解决了,没想到一段时间后下一个问题又来了。

扩展:关于 ws 和 wss

WebSocket可以使用 ws 或 wss 来作为统一资源标志符,类似于 HTTP 或 HTTPS。其中 ,wss 表示在 TLS 之上的 WebSocket,相当于 HTTPS。默认情况下,WebSocket的 ws 协议基于Http的 80 端口;当运行在TLS之上时,wss 协议默认是基于Http的 443 端口。说白了,wss 就是 ws 基于 SSL 的安全传输,与 HTTPS 一样样的道理。所以,如果你的网站是 HTTPS 协议的,那你就不能使用 ws:// 了,浏览器会 block 掉连接,和 HTTPS 下不允许 HTTP 请求一样。

  h5客户端改成wss连接后,测试发现还是无法正常游戏。无奈,再次打开浏览器面板,果然,又看到一个新的问题。

WebSocket connection to ‘wss://{ip}:{port}/‘ failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR

  之前在Http的情况下,客户端一直是用ip+port的形式来连接服务端,当然了也不会出现什么问题。很明显,在更改成Https后,若还是以这种方式连接服务端,浏览器就会报 SSL 协议错误,这很明显就是证书的问题。如果这时候还用 IP + 端口号 的方式连接 WebSocket ,是根本就没有证书存在的(即使我们在Nginx配置了SSL证书,但这种方式其实是不会走Nginx代理的),所以在生成环境下,更推荐大家用域名的方式来连接。于是,立刻又联系前端,再一次做更改,修改为 wss://{域名}/ 进行连接。我以为这样就真的解决了,没想到还是too young too simple,没一会下个问题又来了,测试反馈的结果还是不可以,第三次打开浏览器控制面板,果然又是一个新的错误信息。

WebSocket connection to ‘wss://{域名}/‘ failed: Error during WebSocket handshake: Unexpected response code: 400

  看到这个错误信息后,确定这是服务端返回的400响应。既然可以请求到服务端,就说明客户端这边是没有问题的,那么问题最可能出在客户端和服务端之间。由于中间层使用了Nginx做转发,所以导致服务端无法知道这是一个合法的WebSocket请求。于是立刻查找了网上资料,在Nginx配置文件加入了以下配置,成功解决了这个问题。

    server {location / {proxy_pass http://localhost:{port};proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";}}

  接着,连忙拿域名进行再次连接测试,终于看到了101 Switching Protocols的响应Status Code。就这样,也算是终于解决完在 HTTPS 下以 wss://{域名}/ 的方式连接 WebSocket的一系列问题。不过,最后这其中还有一个小问(插)题(曲)。

关于Nginx中的WebSocket配置

   自1.3 版本开始,Nginx就支持 WebSocket,并且可以为 WebSocket 应用程序做反向代理和负载均衡。WebSocket 和 HTTP 是两种不同的协议,但是 WebSocket 中的握手和 HTTP 中的握手兼容,它使用 HTTP 中的 Upgrade 协议头将连接从 HTTP 升级到 WebSocket,当客户端发过来一个 Connection: Upgrade请求头时,其实Nginx是不知道的。所以,当 Nginx 代理服务器拦截到一个客户端发来的 Upgrade 请求时,需要我们显式的配置Connection、Upgrade头信息,并使用 101(交换协议)返回响应,在客户端、代理服务器和后端应用服务之间建立隧道来支持 WebSocket。
   当然,还需要注意一点,此时WebSocket 仍然受到 Nginx 缺省为60秒的 proxy_read_timeout 配置影响。这意味着,如果你有一个程序使用了 WebSocket,但又可能超过60秒不发送任何数据的话,那么需要增大超时时间(配置proxy_read_timeout),要么实现一个Ping、Pong的心跳消息以保持客户端和服务端的联系。使用Ping、Pong的解决方法有额外的好处,如:可以发现连接是否被意外关闭等。

  关于最后的这个小问题,主要是在对Nginx配置的时候将location=/的请求都进行了proxy_pass(转发)。由于h5客户端的文件打包成静态文件后,存放在服务器的指定目录下(这里假设在/root/html/static/路径下),这也就导致这种配置的情况下Nginx无法正常代理指定目录下的客户端文件。于是再一次修改配置文件,添加location配置,最终完美解决所有问题。

    location /static/ {root /root/html;}

4. 写在最后

  事故一波三折,现在回想起当时,也是一把辛酸史,一把辛酸泪(累)啊。所以仅以此文,记录下我的填“坑”过程。

记录一次迁移 wss WebSocket 的事故相关推荐

  1. (转)记录一次迁移 wss WebSocket 的事故

    [转载一下]   今天是2018年04月21日.   过去的这一个多月里,我的工(开)作(发)任务转战回了游戏.短短的一个月里,催着输出两款h5游戏,再加上对接.联调,想想真是够辛(ku)苦(bi)的 ...

  2. 【详细说明】nginx反向代理wss websocket

    [Nginx那些事]系列 [Nginx那些事]nginx 安装及常用指令 [Nginx那些事]Nginx 配置文件说明 [Nginx那些事]nginx原理解析 [Nginx那些事]nginx配置实例( ...

  3. apache 配置 wss websocket打开握手超时_「Web应用架构」WebSocket介绍和WebSocket API

    WebSocket支持在客户端和服务器之间双向的.面向消息的文本和二进制数据流.它是浏览器中最接近原始网络套接字的API.除了WebSocket连接也不仅仅是一个网络套接字,因为浏览器在一个简单的AP ...

  4. php socket wss,websocket客户端无法建立wss连接

    hi,我在编译swoole时加入了--enable-openssl,也在websocket_server.php中开启了open_ssl选项,但是客户端仍然无法连接到wss,以下是日志: {{{ [/ ...

  5. [记录]Git仓库迁移

    从A仓直接将代码迁移至B仓,以master为例 查看当前项目远程仓情况 $ git remote origin$ git remote -v origin http://gitlan.xxxxx.gi ...

  6. 记录MySQL8.0迁移data文件夹所引发的问题

    前述 这次是接到一个新需求,需要将原本磁盘的数据转移到新的磁盘上,原本是一个复制粘贴然后修改配置的小事,却足足折腾了一天,为此,特意将此情况记录下,以此来加深自己的印象,同时也为大家在解决此类问题时提 ...

  7. GIT安装与使用记录_已迁移

    为什么80%的码农都做不了架构师?>>>    GIT与界面的安装 git base 下载地址  安装教程 link (百度云也有) tortoisegit 下载地址  link 如 ...

  8. WebSocket实战之四WSS配置

    一.前言 上一篇文章WebSocket实战之三遇上PAC  ,碰到的问题只能上安全的WebSocket(WSS)才能解决,配置证书还是挺麻烦的,主要是每年都需要重新更新证书,我配置过的证书最长有效期也 ...

  9. 凌晨1点突发致命生产事故!看的我惊心动魄…

    文章转载自公众号  纯洁的微笑 , 作者 微笑很纯洁 "有一个读者问我:你认为一个程序员具备什么样的能力,才算得上是厉害的程序员?我答:拥有解决问题的能力的程序员.这个回答貌似有点抽象,不要 ...

最新文章

  1. 我看过的Java方面的好文章
  2. 系统访问慢的几个原因
  3. java对托盘加监听右击报错_java实现系统托盘示例
  4. 去重之后统计条数_BOPET:12的普通包装膜到底去哪了?
  5. 高端网站建设css3动画响应式模板_网站建设中整站定制与模板建站存在着什么差异...
  6. 信息系统管理十大知识领域
  7. Ajax中的url使用规则
  8. 2019全球PostgreSQL生态报告出炉,PG为何从RDBMS中脱颖而出?丨文末送书
  9. windows10安装更新很慢ndows,Windows 10最近更新可能会使电脑速度变慢 但有解决办法...
  10. wust2012级软件工程新生经验交流会草稿
  11. python中单词个数_python 统计单词个数
  12. java验证码图片工具类_工具类:VerifyCode.java:图片验证码
  13. *理解JavaWeb目录结构
  14. 约束(Constraint)SQL约束有哪几种?【常用的约束】【有例子】【非空约束】【唯一约束】【主键约束】【外键约束】【检查约束】
  15. 使用yaml文件指定 kata 安全容器配置相关参数
  16. uni-app项目Android离线打包UrlSchemes设置
  17. 《学习笔记》面向对象(四)多态,instanceof关键字,抽象
  18. Unity优化 详谈GetComponent
  19. 史上最全的整数分解方法(包含经典的分苹果问题)
  20. 环境监测 | 温度敏感性药品不同储运容器的温度监测方法

热门文章

  1. 缓存和数据库如何保证一致性
  2. 我写了个随机点名系统,非常好用~ 我给它取名叫做——随机点名系统
  3. 吐血熬夜整理!2020最全web前端面试题!!!你想要的我这都有!!!
  4. CMNET和CMWAP区别
  5. 【OpenCV入门指南】第十篇 彩色直方图均衡化
  6. Matlab Simulink 代数环相关问题
  7. ECMAScript6语法
  8. Python 使用Opencv读写视频(Opencv教程二)
  9. FaceBoxes: 高精度的CPU实时人脸检测器
  10. 网络编程进阶---- hamc模块 socketserver模块验证合法性 两者进行通信连接