一、前言
之前项目中有一套http环境下的webSocket前后台项目(ws协议); 突然测试环境要切换成https,导致webSocket通信遇到了问题(wss协议)。
以下是遇到的问题与解决方法个人总结。
二、问题与解决方法
1.问题概述: vconsole中只报错WebSocket Open Error,但是没有具体错误原因。
问题详情: 前端项目是vue移动端app项目,其中有vconsole可以查看debug时控制台打印的信息; 切换为https后,前端代码执行建立webSocket连接的代码时,建立失败,报错WebSocket Open Error(这个错误是自己定义的),但是没有错误原因; 此时后台没有任何日志打印。
解决方法: 想办法把app访问的网页url获取到,使用chrome浏览器访问,在F12中,找到了错误信息:
SecurityError: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
翻译为, 可能无法从通过HTTPS加载的页面启动不安全的WebSocket连接。
终于找到了报错语句,进入下一步。
2.问题描述: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS
问题详情: 之前在http环境,前端建立webSocket连接使用的url是没有问题的,样例为:
ws://123.123.123.123:20000/webSocket
而现在,由于是 https环境,因此再使用 ws协议,就会报这个错。
解决方法: 把ws改为 wss即可,样例如下:
wss://123.123.123.123:20000/webSocket
3.问题描述: 修改为wss后,依然无法建立webSocket连接,依然无法从vconsole中找到错误详情;使用chrome进行测试,才发现新的错误信息。
问题详情: 新的错误如下:
im.js:1993 WebSocket connection to 'wss://123.123.123.123:20000/webSocket' failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID
其中,im.js是前端项目中的一个js文件; 错误信息是: net::ERR_CERT_COMMON_NAME_INVALID
解决方法: 这个错误大概意思是说,用一个错误的域名访问了某个节点的https资源(可能是Host错误导致)。
(1)分析发现,chrome打开的前端测试页面的 url为域名形式的,样例如下:
https://xxx.xxx.com/result.html
(2)而在这个result.html页面中,引入了im.js文件;在im.js文件中,执行了建立webSocket连接的前端代码;建立连接代码中,访问后台webSocket的 url为IP形式的:
wss://123.123.123.123:20000/webSocket
(3)虽然服务器的ip就是123.123.123.123,域名就是xxx.xxx.com ,但是由于服务器是https的,https证书中配置的是*.xxx.com (证书中配置的域名应该是不能自己修改的); chrome中访问https://xxx.xxx.com ,会提示连接是 安全的; chrome中访问https://123.123.123.123 ,会提示连接是 不安全的。
(4)应该是,由于chrome测试时,当前页面url为https://xxx.xxx.com ,因此发送报文的Host 参数也为xxx.xxx.com ; 而此时发送wss://123.123.123.123:20000/webSocket 的请求,就会因为Host与请求资源的url不一致导致报错(123.123.123.123 != xxx.xxx.com),即使服务器的ip与域名就是123.123.123.123 与xxx.xxx.com也不行。
(5)所以,把 wss的url从ip改为域名,才解决了 net::ERR_CERT_COMMON_NAME_INVALID问题。样例如下:
//这个不行
//wss://123.123.123.123:20000/webSocket
//这个可以
wss://xxx.xxx.com:20000/webSocket
三、其它笔记
1.本次服务器从http变为https 遇到的问题中(ws变为wss) ,大部分是前端需要修改的; Java后台WebSocket服务端的代码不需要修改,还是用的ws连接时的代码 。(Java后台WebSocket服务端代码不区分ws与wss)**
Java后台WebSocket代码主要语句如下:
//这个配置webSocket连接用的url
@ServerEndpoint("/webSocket")
//连接建立,这个方法会被调用
@OnOpen
public void onOpen(WsSession session, EndpointConfig config){//自己写处理逻辑}
//连接关闭,这个方法会被调用
@OnClose
public void onClose(){/*自己写处理逻辑*/}
//前端给后台发来byte数组格式的消息,这个方法会被调用
@OnMessage
public void onMessage(byte[] message){/*自己写处理逻辑*/}
//前端给后台发来String格式的消息,这个方法会被调用
@OnMessage
public void onMessage(String message){/*自己写处理逻辑*/}
//前端给后台发来String格式的消息,这个方法会被调用
@OnError
public void onError(WsSession session, Throwable error){/*自己写处理逻辑*/}
2.从http变为https 遇到的问题中(ws变为wss) ,nginx的配置也不用专门为https修改,使用之前http(ws)时的即可。
nginx可用样例如下:
upstream websocket_cluster {
server 10.1.2.3:20000;
}
server {
listen 20000;
server_name 123.123.123.123;
#因为是测试服务器,虽然是https,但是不用专门配置ssl等参数也可以使用
#ssl on;
access_log /home/user/nginx/logs/websocket.log main;
error_log /home/user/nginx/logs/websocket.error.log;
location /webSocket {
expires -1;
proxy_set_header Host $http_host;
proxxy_pass http://websocket_cluster/webSocket;
proxy_redirect off;
proxy_read_timeout 18000;
proxy_connect_timeout 18000;
proxy_send_timeout 18000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 2g;
proxy_http_bersion 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
说明: (1)前端访问wss://xxx.xxx.com:20000/webSocket 时,dns服务器会把xxx.xxx.com转为123.123.123.123,然后就会访问到这个nginx
(2)根据nginx的配置,20000端口的请求,访问的后缀为/webSocket ,可以转发到http://websocket_cluster/webSocket ,即http://10.1.2.3:20000/webSocket
(3)Java的WebSocket后台部署在10.1.2.3 服务器上,开启的端口是20000 ,使用了注解@ServerEndpoint("/webSocket") ,然后收到前端建立webSocket连接的请求后,@OnOpen 注解标注的方法就会被执行了
(4)Java后台后续收到前端发来的消息后,@OnMessage 注解标注的方法就会被执行了。
(5)Java后台可以获取到WsSession对象,用来主动给前端发送消息。(这也是webSocket的主要功能,后台主动给前端发消息)
(6)nginx日志中,发现,建立webSocket连接时,日志中并不会打印信息;只有关闭webSocket连接时,日志中才会增加一行记录。
(7)nginx中,webSocket主要的配置就是这两个:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
|