13

我正在尝试socket.io同时处理httphttps连接,但似乎以我当前的配置它只能在其中一个上工作。

使用以下配置选项,它可以通过 访问我的应用程序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});

当然,如果我分别使用服务器和客户端的和功能上httphttps选项来切换,我会得到相反的结果,例如它可以通过而不是通过访问。.listen.connecthttphttps

怎么可能做到这一点?我需要它主要是因为它与 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 
4

5 回答 5

19

我相信问题在于您在服务器端和客户端设置 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;
  });

以下是服务器的关键点:

  1. 需要 socket.io 但不要调用它的监听方法(假设已经需要 http 和 https)。相反,只需保留参考。(var ioServer = require('socket.io'))
  2. 创建您的 http 和 https 服务器
  3. 创建一个新的 ioServer 实例
  4. 绑定您的 http 和 https 服务器 (.listen)
  5. 将 http&https 服务器实例附加到 io 实例。(.listen 是 .attach 的别名)
  6. 设置 io 事件。

和客户端(玉语法,但你明白了):

  1. 包含 socket.io 脚本标签
  2. 调用 io 并捕获参考
  3. 设置您的事件处理程序

在客户端上,您不需要调用 io.connect()。此外,我不确定您的选择。看起来您有一个错字 (, ,),我在 1.0 文档中找不到任何对 secure: true 的引用。

于 2014-06-25T22:23:55.740 回答
6

可以说,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”模块变得多余。

于 2014-03-25T17:08:58.320 回答
3

我做了类似的事情,它需要两个 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。

于 2013-06-24T22:26:43.183 回答
1

我使用不同的方法解决了这些问题,我将服务器配置为仅支持未加密的传输,并用于stunnelhttps 支持。

有关如何安装的信息,stunnel您可以查看此帖子

然后,使用以下 con 配置:

#/etc/stunnel/stunnel.conf
cert = /var/www/node/ssl/encryption.pem
[node]
accept = 443
connect = 80

最后,我使用以下内容连接客户端:

var socket = that.socket = io.connect('//'+server);

这将自动检测浏览器方案并相应地使用 http/https 进行连接。

于 2014-03-09T18:31:39.623 回答
0

我猜跨源请求可能是您收到错误的原因。协议的改变被认为是领域的改变。因此,对于通过http服务器访问https服务器(连接到它的 websocket 服务器)提供的页面可能会引发安全错误。请参阅此处有关如何在 express 中启用 CORS 的示例。

此外,您应该*将标题更改为http://serverAddress , https://serverAddress. 允许所有站点不是一个好主意,将其用于测试。

如果您尝试在您的服务器httphttps服务器之间进行 AJAX,情况也是如此。请发布错误,以确保它。

于 2013-06-27T04:40:35.693 回答