node.js socket.io 同时使用 http 和 https

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17285180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 14:53:22  来源:igfitidea点击:

Use both http and https for socket.io

node.jssslexpresssocket.io

提问by Vassilis Barzokas

I am trying to make socket.io work both on httpand httpsconnections, but it seems that with my configuration it can work only on one of them.

我正在尝试使 socket.io 在连接httphttps连接上都能工作,但似乎通过我的配置它只能在其中之一上工作。

With the below config options socket.io can access my application through https, but when trying to access through regular httpit cannot connect and i receive errors.

使用以下配置选项,socket.io 可以通过 访问我的应用程序https,但是当尝试通过常规访问时,http它无法连接并且我收到错误消息。

    var app = express()
  , http= require('http').createServer(app)
  , https = require('https').createServer(options, app)
  , io = require('socket.io').listen(https, { log: false })

And later i have this

后来我有了这个

http.listen(80, serverAddress);
https.listen(443, serverAddress);

On client side i have this:

在客户端,我有这个:

<script src='/socket.io/socket.io.js'></script>

var socket = io.connect('https://<%= serverAddress %>', {secure: true, 'sync disconnect on unload' : true});

Of course if i switch the http with the https options on the .listenand .connectfunctions of the server and the client respectively i am having the reverse results. Socket.io can access through http and not through https.

当然,如果我分别在服务器和客户端的.listen.connect功能上使用 https 选项切换 http,我会得到相反的结果。Socket.io 可以通过 http 而不是通过 https 访问。

How is it possible to achieve this? I need it mostly because it is about a facebook app, so it must provide both http and https connection options according to facebook rules.

怎么可能做到这一点?我需要它主要是因为它是关于 facebook 应用程序的,所以它必须根据 facebook 规则提供 http 和 https 连接选项。

Edit: In case it helps about the problem, the error i am receiving is the below

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

Failed to load resource: the server responded with a status of 404 (Not Found) http://DOMAIN/socket.io/socket.io.js

And because of this i get others such as:

正因为如此,我得到了其他人,例如:

Uncaught ReferenceError: io is not defined 

回答by a_arias

I believe the problem is in your way of setting up socket.io on the server side andon the client.

我相信问题在于您在服务器端客户端设置 socket.io 的方式。

Here's how I made it work (just for you).

这是我如何使它工作(只为你)。

Server:

服务器:

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);
});

Client:

客户:

script(src='/socket.io/socket.io.js')
script.
  var socket = io();
  socket.on('update', function(update){
    document.getElementById('update').innerHTML = update;
  });

Here are the key points for the server:

以下是服务器的关键点:

  1. require socket.io but don't call it's listen method yet (assuming http and https are already required). Instead, just keep the reference. (var ioServer = require('socket.io'))
  2. create your http & https server
  3. create a new instance of ioServer
  4. bind your http and https servers (.listen)
  5. attach http&https server instances to the io instance. (.listen is an alias for .attach)
  6. setup io events.
  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事件。

And the client (jade syntax but you get the idea):

和客户端(jade 语法,但你懂的):

  1. include socket.io script tag
  2. call io and capture reference
  3. setup your event handlers
  1. 包括 socket.io 脚本标签
  2. 调用 io 并捕获参考
  3. 设置您的事件处理程序

On the client you don't need to call io.connect(). Furthermore, I'm not sure about your options there. It looks like you have a typo (, ,) and I can't find any reference to secure: true in the 1.0 documentation.

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

回答by Otto G

Arguably, the node.js server object for HTTP and HTTPS ought to be given the capability to listen on an arbitrary number of ports and interfaces, with and without SSL, but this does not seem to currently be implemented. (I was able to get one server to listen on two ports by passing a second server that had no request listener as the "handle" argument to server.listen(handle, [callback]) interface, in addition to server.listen(port, [hostname], [backlog], [callback]), but it did not work with SSL/non-SSL servers mixed.)

可以说,用于 HTTP 和 HTTPS 的 node.js 服务器对象应该能够在使用和不使用 SSL 的情况下侦听任意数量的端口和接口,但目前似乎还没有实现。(除了 server.listen(port , [hostname], [backlog], [callback]),但它不适用于混合 SSL/非 SSL 服务器。)

The stunnelworkaround already mentioned is of course a viable option, but if it is not desirable to install a separate piece of software (to avoid non-node.js dependencies), the same tunneling can be achieved natively in node.js instead (assuming HTTP on port 80 and HTTPS on port 443):

已经提到的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);

This will have the same effect as using stunnel. In other words, it will avoid the need for two separate socket.io server instances, while also making the node.js "https" module redundant.

这将具有与使用stunnel相同的效果。换句话说,它将避免需要两个单独的 socket.io 服务器实例,同时也使 node.js “https”模块变得多余。

回答by MrTorture

I have done something similar and it required two socket.io instances. Something like this:

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

Of course that you will need to inject messages twice: for both socket.io instances.

当然,您需要两次注入消息:对于 socket.io 实例。

A good option is delegate SSL handling to stunneland forget about SSL in your code.

一个不错的选择是将 SSL 处理委托给stunnel并忘记代码中的 SSL。

回答by Kuf

I solved the problems using a different approach, I configured the server to support only unencrypted transport, and used stunnelfor the https support.

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

For information on how to install stunnelyou can check this post.

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

Then, used the following con configuration:

然后,使用以下配置:

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

Finally, I used the following to connect the clients:

最后,我使用以下方法连接客户端:

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

This will auto detect the browser schemeand connect using http/https accordingly.

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

回答by user568109

I am guessing Cross origin requests could be the reason why you are getting errors. Change in protocol is considered change in domain. So for page served via httpserver accessing httpsserver (websocket server attached to it) may throw security errors. See an example hereon how to enable CORS in express.

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

Also you should change *in the header to http://serverAddress , https://serverAddress. Allowing all sites is not a good idea, use it for testing.

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

The same is true if you are trying to AJAX between your httpand httpsservers. Please post the errors, just to be sure about it.

如果您尝试在您的服务器httphttps服务器之间使用 AJAX,情况也是如此。请张贴错误,以确保它。