【问题标题】:Use both http and https for socket.io对 socket.io 使用 http 和 https
【发布时间】:2013-06-21 12:47:06
【问题描述】:

我正在尝试使socket.iohttphttps 连接上都工作,但似乎以我当前的配置它只能在其中一个上工作。

使用以下配置选项,它可以通过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 函数上分别切换httphttps 选项,我会得到相反的结果,例如它可以通过http而不是https访问。

如何实现这一目标?我需要它主要是因为它与 Facebook 应用程序有关,因此它必须根据 Facebook 的规则同时提供 httphttps 连接选项。

编辑:如果它有助于解决问题,我收到的错误是这样的:

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 

【问题讨论】:

    标签: node.js ssl express socket.io


    【解决方案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。允许所有站点不是一个好主意,将其用于测试。

    如果您尝试在 httphttps 服务器之间进行 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,所以我总是需要连接端口 9876 http.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;
        });
      

      以下是服务器的关键点:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-21
        • 2016-01-20
        • 2020-01-31
        • 2015-02-16
        • 2015-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-17
        相关资源
        最近更新 更多