关于WebSocket,维基百科是这样介绍的:
以前,很多网站为了实现实时推送技术,所用的技术都是轮询。轮询是在特定的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端。这种传统的模式带来的缺点很明显,即浏览器需要不断的向服务器发出请求,然而HTTP请求包含较多的请求头信息,而其中真正有效的数据只是很小的一部分,显然这样会浪费很多的带宽等资源。在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket是一种在单个TCP连接上进行全双工通讯的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
在我的项目中,有个学习动态的模块,因为本身维护+开发,项目刚上没多久,初期也比较冷清。所以想弄个实时的学习动态,所有在线用户操作,都会进行广播。后台在某些场景下推送一些消息。不过因为冷清和网页美观方面(不知道放在哪个位置好看等因素),所以没有做。
不过在浏览一个前端大神的博客中,发现他的网站做的特别好 —传送门— ,一个是页面切换。一个是实时在线人数。所以把广播改成后台实时在线人员监控。
原理:用websocket 进行长链接。
故事
最开始是在本地测试完毕。然后上线后出了问题
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://
安全协议了,于是立即修改前端代码,把连接服务端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 请求一样。
前端改成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 {
listen 443 ssl;
server_name wss.wkcto.com ;
ssl_certificate cert/wss.wkcto.com/cert-1542100842869_wss.wkcto.com.crt;
ssl_certificate_key cert/wss.wkcto.com/cert-1542100842869_wss.wkcto.com.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# root /usr/share/nginx/html/wss;
location /websocket{
proxy_pass http://localhost:1990;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
接着,连忙拿域名进行再次连接测试,终于看到了101 Switching Protocols
的响应Status Code
。就这样,也算是终于解决完在 HTTPS 下以 wss://{域名}/websocket
的方式连接 WebSocket
的一系列问题。
参考文章:https://blog.csdn.net/qq_28804275/article/details/80891921