WebSocket长连接实现及应用场景

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
现在,很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源,Comet本质上也是轮询,但是在没有消息的情况下,服务器先拖一段时间,等到有消息了再回复。这个机制暂时地解决了实时性问题,但是它带来了新的问题:以多线程模式运行的服务器会让大部分线程大部分时间都处于挂起状态,极大地浪费服务器资源。另外,一个HTTP连接在长时间没有数据传输的情况下,链路上的任何一个网关都可能关闭这个连接,而网关是开发者们不可控的,这就要求Comet连接必须定期发一些ping数据表示连接“正常工作”。所以,HTML5推出了WebSocket标准,让浏览器和服务器之间可以建立无限制的全双工通信,任何一方都可以主动发消息给对方。WebSocket协议能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

首先,WebSocket并不是全新的协议,而是利用了HTTP协议来建立连接;其次,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:

GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13

#该请求和普通的HTTP请求有几点不同:
GET请求的地址不是类似/path/,而是以ws://开头的地址;
请求头Upgrade: websocket和Connection: Upgrade表示这个连接将要被转换为WebSocket连接;
Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;
Sec-WebSocket-Version指定了WebSocket的协议版本。

随后,服务器如果接受该请求,就会返回如下响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string

#该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。
#版本号和子协议规定了双方能理解的数据格式,以及是否支持压缩等等。如果仅使用WebSocket的API,就不需要关心这些。

判断浏览器是否支持WebSocket:

var is_support = ("WebSocket" in window);
if (is_support) {
    // 打开一个 web socket
    var ws = new WebSocket("ws://服务地址");
    //  Web Socket 已连接上
    ws.onopen = function() {
        // 使用 send() 方法向服务器发送数据
        ws.send(数据);
        console.log("数据发送中...");
    };
    //  接受服务器数据
    ws.onmessage = function(e) {
        var received_msg = e.data;
        console.log("数据已接收...");
    };
    //  连接关闭
    ws.onclose = function() {
        // 关闭 websocket
        console.log("连接已关闭...");
    };
} else {
    // 浏览器不支持 WebSocket
    alert("您的浏览器不支持 WebSocket!");
}

首先更新系统(CentOS):

yum -y update

这里为了简化步骤使用v2ray官方的一键安装脚本:

bash <(curl -L -s https://install.direct/go.sh)

设置开启启动:

systemctl enable v2ray

安装EPEL源,系统环境为CentOS7X64,倘若EPEL源出现故障不能生效,解决办法是先卸载再重新装一遍,先查找:

rpm -qa | grep epel-release

然后卸载:

rpm -e epel-release-7-11.noarch

重新安装EPEL:

yum -y install epel-release

安装certbot用于签发SSL证书:

yum -y install certbot

这里就先把SSL证书申请好:

certbot certonly --standalone -d example.com

倘若一切正常,申请好的证书和私钥路径应如下所示:

/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem

现在添加一个Nginx安装源:

vi /etc/yum.repos.d/nginx.repo

写入:

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

安装Nginx:

yum -y install nginx

设置开机启动:

systemctl enable nginx

新建一个Nginx站点配置文件:

vi /etc/nginx/conf.d/v2ray.conf

写入:

server {
    listen       443 ssl;
    server_name  example.com;

    ssl_certificate    /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key    /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    error_page 497  https://$host$request_uri;

location /ray {
    proxy_pass       http://127.0.0.1:10000;
    proxy_redirect             off;
    proxy_http_version         1.1;
    proxy_set_header Upgrade   $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host      $http_host;
    }
}

备份v2ray的默认配置文件:

cp /etc/v2ray/config.json /etc/v2ray/config.jsonbak

清空配置文件的内容:

echo "" > /etc/v2ray/config.json

编辑配置文件:

vi /etc/v2ray/config.json

写入如下配置:

{
  "inbounds": [
    {
      "port": 10000,
      "listen":"127.0.0.1",
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "你的UUID",
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
        "path": "/ray"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

注:上面的配置内,UUID自行使用网站生成一个:https://www.uuidgenerator.net

#形如:
723c124f-0639-49d5-9f4e-162e4d749577
184e275c-ca36-48d9-81e6-fd360c1b80dc
2df4d292-0b12-4458-9a9f-0c7185103f53
21d8db69-2ae9-4bc7-baed-080477ca3551
70c9d4a2-ff4f-4b6e-a76a-949d00a4014a
9bdfe234-ef94-4058-8fd3-c71c155cf79c
d9af4afa-fe4d-4ed7-91ab-b3256b887693
1fb43386-47ee-4a5d-89f2-7678e4f1c0a6
1725972e-3e73-4b0b-b672-95625b55b8ec
26585937-d27d-451c-94c6-13a2ed34679a

全部完成之后,关闭系统防火墙firewall,也可以卸载CentOS默认防火墙firewall安装iptables:

systemctl stop firewalld.service

关于启用iptables的如下步骤,如无必要性可跳过。

systemctl disable firewalld.service
yum -y install iptables-services
vi /etc/sysconfig/iptables
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 56789 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 21 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 3306 -j DROP
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 11211 -j DROP
-A INPUT -p udp -m udp --dport 11211 -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

按 :wq 保存退出后,重启iptables,并设置为开机启动:

service iptables restart
systemctl enable iptables.service

同时关闭SELinux(流量内网转发的限制):

vi /etc/selinux/config
SELINUX=disabled
setenforce 0

现在启动v2ray和nginx:

systemctl start v2ray
systemctl start nginx

使用如下命令查看这两个软件目前是否运行正常:

systemctl status v2ray
systemctl status nginx

路径(path)按照上述/ray填写,底层传输安全填写tls,Camouflage Domain(host)填写建议留空,即可完成配置。

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注