对 socket.io 使用 http 和 https
Posted
技术标签:
【中文标题】对 socket.io 使用 http 和 https【英文标题】:Use both http and https for socket.io 【发布时间】:2013-06-21 12:47:06 【问题描述】:我正在尝试使socket.io
在http
和https
连接上都工作,但似乎以我当前的配置它只能在其中一个上工作。
使用以下配置选项,它可以通过https
访问我的应用程序,但是当尝试通过http
访问它时,它无法连接并且我收到错误消息:
var app = express()
, http= require('http').createServer(app)
, https = require('https').createServer(options, app)
, io = require('socket.io').listen(https, log: false )
后来我有了这个:
http.listen(80, serverAddress);
https.listen(443, serverAddress);
在客户端我有这个:
<script src='/socket.io/socket.io.js'></script>
var socket = io.connect('https://<%= serverAddress %>', secure: true, 'sync disconnect on unload' : true);
当然,如果我在服务器和客户端的.listen
和.connect
函数上分别切换http
和https
选项,我会得到相反的结果,例如它可以通过http
而不是https
访问。
如何实现这一目标?我需要它主要是因为它与 Facebook 应用程序有关,因此它必须根据 Facebook 的规则同时提供 http
和 https
连接选项。
编辑:如果它有助于解决问题,我收到的错误是这样的:
Failed to load resource: the server responded with a status of 404 (Not Found) http://DOMAIN/socket.io/socket.io.js
因此,我得到了其他人,例如:
Uncaught ReferenceError: io is not defined
【问题讨论】:
【参考方案1】:我做过类似的事情,它需要两个 socket.io 实例。像这样的:
var express = require('express');
var oneServer = express.createServer();
var anotherServer = express.createServer();
var io = require('socket.io');
var oneIo = io.listen(oneServer);
var anotherIo = io.listen(anotherServer);
当然,您需要两次注入消息:对于两个 socket.io 实例。
一个不错的选择是将 SSL 处理委托给 stunnel 并在您的代码中忘记 SSL。
【讨论】:
来自http
的客户端能否与连接到https
的人通信?关于stunnel
,我已经尝试过,http-proxy
,但我无法让它们正常工作。你能举个例子吗?
抱歉耽搁了。是的,stunnel 负责 SSL 并透明地解密连接到 https 端口 (443) 的客户端,使它们看起来像连接到 express http 端口 (80)。
这里有一个简单的 stunnel 配置的要点,它侦听 443 并将所有传入流量转发到同一主机的端口 80:gist.github.com/mbenedettini/5911415【参考方案2】:
我猜跨源请求可能是您收到错误的原因。协议的改变被认为是领域的改变。因此对于通过http
服务器访问https
服务器(连接到它的websocket 服务器)提供的页面可能会引发安全错误。有关如何在 express 中启用 CORS,请参阅示例 here。
您还应该将标题中的*
更改为http://serverAddress , https://serverAddress
。允许所有站点不是一个好主意,将其用于测试。
如果您尝试在 http
和 https
服务器之间进行 AJAX,情况也是如此。请发布错误,以确保正确。
【讨论】:
谢谢,我添加了收到的错误。我尝试启用CORS,然后我明白我在客户端的head
中包含socket.io 的方式应该改变,因为请求总是针对htpps 而不是相对于url。但它并没有很好地工作,我遇到了一些其他错误,例如 NETWORK_ERR: XMLHttpRequest Exception 101
,这一定是因为我正在使用 Websockets,如果不可用,XHR 轮询。
这个错误是在你添加的有问题的错误之后出现的,还是你添加的那些错误被这个错误替换了。
现在发生的错误是由于同源策略。您能说出导致此错误的行吗?也许 CORS 请求失败是针对某些 url。【参考方案3】:
我使用不同的方法解决了这些问题,我将服务器配置为仅支持未加密的传输,并使用stunnel
来支持 https。
有关如何安装stunnel
的信息,您可以查看此post。
然后,使用以下 con 配置:
#/etc/stunnel/stunnel.conf
cert = /var/www/node/ssl/encryption.pem
[node]
accept = 443
connect = 80
最后,我使用以下连接客户端:
var socket = that.socket = io.connect('//'+server);
这将auto detect the browser scheme 并相应地使用 http/https 进行连接。
【讨论】:
我使用了这种方法,但它在我的系统中不起作用,对于 http 所有的东西都可以正常工作,但我不知道如何为 https 配置,我使用了 node、socket io、redis 和 rails,所以我总是需要连接端口 9876http.listen(9876, function() console.log('listening on *:9876'); );
,我在 stunnel.conf 中添加了您的代码。但不工作。你能帮帮我吗?【参考方案4】:
可以说,HTTP 和 HTTPS 的 node.js 服务器对象应该能够监听任意数量的端口和接口,无论是否使用 SSL,但这似乎目前还没有实现。 (除了 server.listen(port , [hostname], [backlog], [callback]),但它不适用于混合的 SSL/非 SSL 服务器。)
已经提到的 stunnel 解决方法当然是一个可行的选择,但如果不希望安装单独的软件(以避免非 node.js依赖项),可以在 node.js 中本地实现相同的隧道(假设端口 80 上的 HTTP 和端口 443 上的 HTTPS):
var fs = require('fs');
var net = require('net');
var tls = require('tls');
var sslOptions =
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
;
tls.createServer(sslOptions, function (cleartextStream)
var cleartextRequest = net.connect(
port: 80,
host: '127.0.0.1'
, function ()
cleartextStream.pipe(cleartextRequest);
cleartextRequest.pipe(cleartextStream);
);
).listen(443);
这与使用 stunnel 的效果相同。换句话说,它将避免需要两个单独的 socket.io 服务器实例,同时也使 node.js “https”模块变得多余。
【讨论】:
【参考方案5】:我认为问题出在您在服务器端和在客户端设置 socket.io 的方式上。
这就是我如何让它工作的(只为你)。
服务器:
var debug = require('debug')('httpssetuid');
var app = require('../app');
var http = require('http');
var https = require('https');
var fs = require('fs');
var exec = require('child_process').exec;
var EventEmitter = require('events').EventEmitter;
var ioserver = require('socket.io');
var startupItems = [];
startupItems.httpServerReady = false;
startupItems.httpsServerReady = false;
var ee = new EventEmitter();
ee.on('ready', function(arg)
startupItems[arg] = true;
if (startupItems.httpServerReady && startupItems.httpsServerReady)
var id = exec('id -u ' + process.env.SUDO_UID, function(error, stdout, stderr)
if(error || stderr) throw new Error(error || stderr);
var uid = parseInt(stdout);
process.setuid(uid);
console.log('de-escalated privileges. now running as %d', uid);
setInterval(function cb()
var rnd = Math.random();
console.log('emitting update: %d', rnd);
io.emit('update', rnd);
, 5000);
);
;
);
app.set('http_port', process.env.PORT || 80);
app.set('https_port', process.env.HTTPS_PORT || 443);
var httpServer = http.createServer(app);
var opts =
pfx: fs.readFileSync('httpssetuid.pfx')
;
var httpsServer = https.createServer(opts, app);
var io = new ioServer();
httpServer.listen(app.get('http_port'), function()
console.log('httpServer listening on port %d', app.get('http_port'));
ee.emit('ready', 'httpServerReady');
);
httpsServer.listen(app.get('https_port'), function()
console.log('httpsServer listening on port %d', app.get('https_port'));
ee.emit('ready', 'httpsServerReady');
);
io.attach(httpServer);
io.attach(httpsServer);
io.on('connection', function(socket)
console.log('socket connected: %s', socket.id);
);
客户:
script(src='/socket.io/socket.io.js')
script.
var socket = io();
socket.on('update', function(update)
document.getElementById('update').innerhtml = update;
);
以下是服务器的关键点:
-
需要socket.io 但不要调用它的listen 方法(假设http 和https 已经是必需的)。相反,只需保留参考。 (var ioServer = require('socket.io'))
创建您的 http 和 https 服务器
创建一个新的 ioServer 实例
绑定您的 http 和 https 服务器 (.listen)
将 http&https 服务器实例附加到 io 实例。 (.listen 是 .attach 的别名)
设置 io 事件。
和客户端(jade 语法,但你明白了):
-
包含 socket.io 脚本标签
调用 io 并捕获参考
设置您的事件处理程序
在客户端上,您不需要调用 io.connect()。此外,我不确定您的选择。看起来您有一个错字 (, ,),我在 1.0 文档中找不到任何对 secure: true 的引用。
【讨论】:
以上是关于对 socket.io 使用 http 和 https的主要内容,如果未能解决你的问题,请参考以下文章
Electron - 实现 http 模块和 socket.io
使用 HTTP keep-alive 和 websockets (socket.io) 时的 TCP 连接数
Socket.io 无法在 nginx + node.js + php 应用程序中连接
将 Socket.IO 与 Apache、Node.js、SSL 和 CloudFlare (HTTP 500) 一起使用