公司游戏需要搞个微信小程序版,然后小程序需要用wss协议,即webSocket+ssl,所以记录一下(踩坑的)过程,方便看到这篇文章的人(虽然已经是超过半年以前的事情,实际上老大来问我的时候已经快忘光了,找点时间记录下来免得下次继续忘)
目录
一、C++实现webSocket升级
首先,webSocket是什么?其实就是一个协议,要求数据按照这个协议的规则来进行发放。关于ws的东西网上一搜一大把,这里就不再赘述,有兴趣的可以自己去看一下,这里推荐http - WebSocket 详解 - 不挑食的程序员 - SegmentFault 思否https://segmentfault.com/a/1190000012948613
写得很详细
然后这里有大佬造的简易轮子,可惜没用上(76条消息) C++实现WebSocket功能及WebSocket协议详解(附代码)_c websocket_贝松的博客-CSDN博客https://blog.csdn.net/qq_39540028/article/details/104493049
首先,相信看这篇文章的都是有升级wss协议需求的,而不是从头开始构建链接,所以就不说那些了,直接从ws开始。ws其实就握手保证一下链接真实可靠,然后通过这个链接双方互相传输消息,最后交换完消息就挥手关掉链接。具体是怎么实现的呢?
首先,在http的基础下,客户端会向服务端提出升级协议,这时候是明文请求的
GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
实际上这里要关心的是两个点,一个是开头的GET,一个是最后的Sec-WebSocket-Key。因为对我实际做的项目来说,需求是能支持同一个服务器接收来自不同客户端的消息,也就是说支持小程序和普通app的混服,而我们自己本身是用的自用协议,所以在客户端开始握手的时候要判断到底是来自于哪里的消息、接下来采用哪种协议去进行通讯。于是乎就直接判断握手消息是否GET开头,是的话后面开始走WS逻辑,否则走我们原来的自用协议。当然假如你有严格要求,或者怕和正在使用的其他协议发生混淆,全部消息检查一遍也是没问题的,上面是ws的 RFC2616的规定,没啥好说的。
而Sec-WebSocket-Key,这是客户端要求服务端做的一个验证,需要在这串东西后面拼接上258EAFA5-E914-47DA-95CA-C5AB0DC85B11这样一个协议约定的字符串,然后把拼接好的内容先sha1算出来一个哈希值,然后将这个哈希值转成base64字符串,到时候回包能用到。这么做主要是为了向客户端保证自己的确收到了这样一个消息,并且针对这个消息做出回复,保证双方不会被偶然链接影响。
好了,收到客户端ws的请求,现在要回复了。
HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
除了Accept以外的东西实际使用没啥好关心的,照样发出去就行了。
二、ssl实现(通过nginx代理)
nginx代理其实就是用nginx中转,让前端只向nginx对外端口发消息,nginx处理之后将数据按指定规则发给后端,这里的处理其实也可以包括ssl握手与加密,将前端发过来的WSS处理成WS包,后端只要专心解WS包就好了。
SSL需要一个证书支持,所谓的证书其实就是一个身份证证明你是你自己。假如是用阿里云之类的服务器的,去申请一个自己对应域名的证书就好了,关于证书的部分可以跳过。但是假如是自机测试调试的话,就需要自己弄一个证书给nginx了。
首先要注意一点,wss访问是访问域名的,而证书也是对域名进行认证的。自己本机上调试的怎么办?改个host自己弄个域名就好。C:\Windows\System32\drivers\etc这个路径,或者你是linux的话就etc/hosts,加个
127.0.0.1 test.game.net
将test.game.net定向到本机ip上,当然你自己改个域名也行,不过下面的步骤都要自己跟着改。
域名弄好之后,下个openssl证书开搞,具体可以查看(88条消息) 关于Nginx配置SSL证书(Https)和WebSocket的wss_wss证书_Shiro to kuro的博客-CSDN博客
记得两个点:
1.nginx域名填刚刚host里面改的域名
2.做证书的时候Common Name一定要填那个域名!
给一下我的conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name test.game.net;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# HTTPS server
#
server {
listen 443 ssl;
server_name test.game.net;
ssl_certificate ../keys/server.pem;
ssl_certificate_key ../keys/server.key;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv2 SSLv3;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
ssl_verify_client off;
#location / {
# root html;
# index index.html index.htm;
#}
location /{
proxy_redirect off;
proxy_pass http://本机IP:想要转发的端口/;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
}
}
}
上面最重要的是HTTPS的部分,内容其实就是监听443接口,认为传入的是ssl加密后信息,转发到你自己ip和端口上,然后让你自己的服务端去监听这个端口。
nginx搭起来了,就可以用在线工具测一下了
WebSocket在线测试工具 (wstool.js.org)
只不过浏览器搞的话,因为你的证书是自己当CA签的,浏览器不会认,要先让浏览器放行你的证书。具体操作方法就是开了nginx之后,浏览器上一下监听的域名,然后应该会说网站证书不安全之类的,点一下把你的自签证书加入可信。不然的话用上面的在线工具会报错