WebSocket的实现以及通过Nginx进行反向代理

2017-01-02 Meng Lei 更多博文 » 博客 » GitHub »

Nginx HTTPS WebSocket

原文链接 http://menglei.tk/2017/01/02/nginx-websocket/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


什么是Websocket

Websocket 是HTML5开始推出的一种新的协议,实现了浏览器与服务端的全双工通信,在使用WebSocket时,,只要和服务端做一个握手(handshaking)动作,浏览器首先要向服务端发起一个特殊的HTTP请求,其头部附加了信息Upgrade: WebSocket,表明这是一个申请协议升级的HTTP请求,服务端解析出来这些信息后,产生一个应答给客户端,这样双方的WebSocket连接就建立起来了,即可形成一条全双工的数据通道,两者之间可以进行互相通信,直到客户端和服务端中的某一方主动关闭连接。

WebSocket出现之前,为了解决浏览器和服务端之间的实时推送问题,采取了很多解决方案,通常使用的是轮询(Polling)和Comet技术,这些方案带来很明显的缺陷就是需要由浏览器主动发出HTTP Request,大量消耗服务器的带宽和资源,所以为了解决这些问题,HTML5推出了WebSocket。

Socket.io 是WebSocket协议的一个拓展,由于浏览器端对WebSocket的支持程度不一,为了能够兼容不同的浏览器,提升用户体验,简化程序员编写代码时的复杂度,Socket.io封装了以上几种不同的实时通讯机制,并实现了统一的接口,在实际应用的过程中,如果浏览器支持WebSocket,就会以WebSocket方式与服务端进行实时数据交互,如果浏览器端不支持WebSocket特性,那么Socket.io会主动进行降级,使用轮询等其他方式。以下为Socket.io兼容的几种不同机制:

  • Adobe® Flash® Socket:通过将Flash插件嵌入到浏览器中而实现的一种Socket通信模式,由于是第三方实现,不在W3C规范内,并且绝大部分手机端浏览器不支持这种方式,所以在逐渐淘汰中。
  • AJAX Long Polling: 所有浏览器都支持,定时向服务器端发送HTTP请求,兼容性广,但是会给服务器带来很大压力,并且不能保证数据的及时更新。
  • AJAX multipart streaming: 在XMLHttpRequest对象上,使用某些浏览器(比如FireFox)支持的multi-part标志,Ajax请求发送给服务端并保持挂起状态,每次需要向客户端发送信息的时候,就寻找一个挂起的HTTP请求进行响应,并且所有的响应请求都会通过统一的连接来写入。
  • Forever Iframe: 该技术设计了一个置于页面中的隐藏Iframe标签,该标签的src属性指向返回服务器端事件的servlet路径,每次在事件到达时,servlet写入并刷新一个新的script标签,该标签内部带有Javascript代码,iframe的内容被附加上这一script标签,标签中的内容就会得到执行,这种方式的缺点是连接和数据都是有浏览器通过HTML标签处理的,你无法知道连接何时在哪一端已经被断开了,并且Iframe标签在浏览器中将逐步被取消。
  • JSONP Polling: JSONP轮询基本上与HTTP轮询一样,不同之处是JSONP可以发出跨域请求。

WebSocket 简单demo(Socket.io实现)

简单客户端:

<head>
    <meta charset="UTF-8">
    <title>WebSocket Demo</title>
    <script src="//cdn.bootcss.com/socket.io/1.7.2/socket.io.min.js"></script>
</head>
<body>
    <h1>WebSocket Demo</h1>

    <script>
        var io = io.connect('http://127.0.0.1:3000');
        io.on('data', function (data) {
            console.log('on server data: ' + data);
        });
    </script>
</body>
</html>

简单服务端:

var app = require('koa')();
var server = require('http').createServer(app.callback());
var io = require('socket.io')(server);
io.on('connection', function (client) {
    client.emit('data', 'Hello WebSocket.');
});
server.listen(3000);    //监听3000端口

我们打开控制台,访问html页面,即可在控制台看到输出的Hello WebSocket字样,即表明服务端已经具备向客户端推送数据的能力。

Nginx反向代理WebSocket

反向代理(Reverse Proxy)是指代理服务器接受互联网上的请求,将请求转发给内部服务器并将结果返回给外部客户端,使用反向代理服务器,有以下几点好处:

  • 可以保护网站安全,所有的访问请求都是先经过代理服务器,然后才到达真正的服务器,如果有网络攻击,可以直接在代理服务器上进行屏蔽,不影响后端真正服务器的运行。
  • 可以将后端服务器上的资源进行缓存,实现某些静态资源的加速访问,减轻后端服务器的负载压力。
  • 充当负载均衡服务器,将访问请求平均地分发给后端服务器,同时自动剥离故障服务器,保证服务平稳运行。

Nginx是一款优秀的轻量级网页服务器,同时也支持反向代理服务器功能,在这里,我们将使用Nginx作为HTTP和WebSocket的反向代理服务器,只要在Nginx的配置文件中添加以下项目即可。

server {
    listen       80;
    server_name  localhost;
    #access_log  logs/host.access.log  main;
    location / {
        proxy_pass http://127.0.0.1:3000/;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Read-IP $remote_addr;
    }
    #error_page  404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

至此,我们只要通过80端口即可访问我们的WebSocket demo了。