Javascript 如何在PHP web服务器和node.js服务器上的Socket.io之间创建握手?

Javascript 如何在PHP web服务器和node.js服务器上的Socket.io之间创建握手?,javascript,php,node.js,express,socket.io,Javascript,Php,Node.js,Express,Socket.io,我在服务器10.0.4.18的node.js4.0上运行了一个websocket,端口为8020。我使用socket.iov1.3、express和express会话实现了websocket 项目定义 我需要能够在socket.io中为从PHP应用程序连接到它的每个用户创建一个会话。在用户发送第一个HTTP请求后,我将使用一个令牌对它们进行身份验证,该令牌将与HTTP请求一起从PHP传递到socket.io 用户通过身份验证后,我需要在socket.io会话中保存一些个人数据,以便以后重用。每次

我在服务器10.0.4.18的node.js4.0上运行了一个websocket,端口为8020。我使用socket.iov1.3、express和express会话实现了websocket

项目定义

我需要能够在socket.io中为从PHP应用程序连接到它的每个用户创建一个会话。在用户发送第一个HTTP请求后,我将使用一个令牌对它们进行身份验证,该令牌将与HTTP请求一起从PHP传递到socket.io

用户通过身份验证后,我需要在socket.io会话中保存一些个人数据,以便以后重用。每次用户刷新PHP应用程序时,socket.io都需要知道已经创建的会话数据

问题

每次用户重新加载/刷新“他/她从何处连接”的PHP页面时,会话数据都会丢失。服务器无法知道该连接属于先前创建的会话XYZ

我不知道如何在PHP和node.js之间创建一个握手,在这里两台服务器可以交换一个唯一的数据来绑定socket.io会话

非常仔细地看问题

我在浏览器中打开了此链接。“这根据路由代码直接从node.js为我创建了一个会话”

然后我使用PHP连接到websocket,现在我可以看到会话没有问题!我能够在浏览器中打开多个选项卡,并且与预期的相同会话也在那里

这一次之所以有效,是因为url建立了一个会话,并将其绑定在我的浏览器和会话之间,从那里我可以使用express-socket.io-session包从socket.io读取/写入会话

但是,如果我不使用url手动创建会话,那么该会话只适用于一个页面加载。每次重新加载页面时,会话都会被破坏,就像它从未存在过一样

问题

当从socket.io连接到websocket时,我需要开发相同的行为

如何在PHP服务器和socket.io之间建立握手,在每次重新加载PHP页面或打开新浏览器选项卡时,这两个服务器都可以将会话数据绑定到正确的用户

这是我的websocket代码

var app = require('express')(),
    https = require('https'),
    fs = require('fs'),
    session = require('express-session'),
    sharedsession = require("express-socket.io-session"),
    fileStore = require('session-file-store')(session),
    base64url = require('base64url'),
    cookieParser = require("cookie-parser"),
    env = require('./modules/config');


var server = https.createServer(
    {
        key: fs.readFileSync('certs/key.pem'),
        cert: fs.readFileSync('certs/cert.pem')
    }, app).listen(env.socket.port, env.socket.host, function () {
    console.log('\033[2J');
    console.log('Websocket is running at https://%s:%s', server.address().address, server.address().port);
});

var io = require('socket.io')(server);

var icwsReq = require('./modules/icws/request.js'),
    icwsConn = require('./modules/icws/connection.js'),
    icwsInter = require('./modules/icws/interactions.js'),
    sessionValidator = require('./modules/validator.js');

var icwsRequest = new icwsReq();
var sessionChecker = new sessionValidator();

var sessionStoreFile = new fileStore({path: './tmp/sessions'});

var clients = {};

var sessionOptions = {
        store: sessionStoreFile,
        secret: env.session.secret,
        name: env.session.name,
        rolling: true,
        saveUninitialized: false,
        resave: true,
        unset: 'keep',
        cookie: {
            maxAge: 60 * 60 * 1000
        }
    };

var sessionMiddleware = session(sessionOptions);

app.use(sessionMiddleware); // session support for the app


//Set access control headers on every express route.
app.use(function (req, res, next){
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
    next();
});


// Use shared session middleware for socket.io
io.use(sharedsession(sessionMiddleware, {
    autoSave: true
})); 

//Middleware for authorizing a user before establishing a connection
io.use(function(socket, next) {

    var myIP = socket.request.socket.remoteAddress || '';
    var token = socket.handshake.query.tokenId || '';
    var session = socket.handshake.session || {};

    if(!session && !token){
        console.log('Log: No session and no token!');
        return next(new Error('No tken/session found'));
    }

    if(!token){
        console.log('Log: token was not found');
        return next(new Error('Token not found'));
    }

    //SessionID should be defined on a page reload
    console.log('IP Address: ' + myIP + '      SessionID: ' + socket.handshake.sessionID);

    //allow any user that is authorized
    if(session && session.autherized && token == session.token){
        console.log('Log: you are good to go');
        return next(new Error('You are good to go'));
    }

    //if the client changed their token "client logged out"
    //terminate the open session before opening a new one
    if (session.autherized && token != session.token){

    var decodedToken = base64url.decode(token);

    sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){

        if(!isValid){
            console.log('Log: token could not be validated!');
            return next(new Error('Token could not be validated!'));
        }

        session.authorized = true;
        session.icwsServer = icws.host;
        session.icwsPort = icws.port;
        session.token = token;
        session.icwsSessionId = null;
        session.icwsToken = null;

        icwsRequest.setConnection(icws.host, icws.port);
        var icwsConnection = new icwsConn(icwsRequest);

        session.save(function(){
            console.log('Log: new connection to websocket!');
            return next();
        });
    });

});


io.on('connection', function (socket) { 

    console.log('Connection is validated and ready for action!');

    var socketId = socket.id;

    if(!socket.handshake.sessionID){
        console.log('sessionId was not found');
        return false;
    }

    var sessionID = socket.handshake.sessionID;
    var userCons = clients[sessionID] || [];

    //Add this socket to the user's connection
    if(userCons.indexOf(socketId) == -1){
        userCons.push(socketId);
    }

    clients[sessionID] = userCons;

    socket.on('chat', function(msg){

        for (var key in clients[sessionID]) {
          if (clients[sessionID].hasOwnProperty(key)) {
            var id = clients[sessionID][key];
            console.log('Client Said: ' + msg);
            io.to(id).emit('chat', {message: 'Server Said: ' + msg});
          }
        }
    });


    socket.on('disconnect', function(msg){
        console.log('Closing sessionID: ' + sessionID);
        var userCons = clients[sessionID] || [];

        var index = userCons.indexOf(socketId);

        if(index > -1){
            userCons.splice(index, 1);
            console.log('Removed Disconnect Message: ' + msg);
        } else {
            console.log('Disconnect Message: ' + msg);
        }

    }); 

    socket.on('error', function(msg){
        console.log('Error Message: ' + msg);
    }); 

});


app.get('/', function (req, res) {
    res.send('welcome: ' + req.sessionID);
});

app.get('/read', function (req, res) {
    res.send('welcome: ' + req.session.name);
});

app.get('/set/:name', function (req, res) {
    req.session.name = req.params.name;
    res.send('welcome: ' + req.session.name);
});
下面是我如何从PHP服务器连接到websocket的

<!doctype html>
<html lang="en-US">
  <head>
    <title>Socket.IO chat</title>
    <meta charset="utf-8">
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>

    <script src="https://10.0.4.18:8020/socket.io/socket.io.js"></script>
    <script type="text/javascript" src="/js/jquery-2.1.0.min.js"></script>
    <script>

        $(function(){
            var socket = io.connect('https://10.0.4.18:8020', {secure: true, port: 8020,  query : 'PHP generated token that will be used to authenticate the user from node.js'});

           //When the "send" button is clicked
            $('#f').click(function(e){
                e.preventDefault();
                var message = $('#m').val().trim();
                if( message  == ''){
                    return false;
                }

                socket.emit('chat', message);
                $('#m').val('');
            });

            socket.on('chat', function(msg){
                $('#messages').append($('<li>').text(msg.message));
            });

        });

    </script>

  </head>
  <body>
    <ul id="messages"></ul>
    <form action="" id="f">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Socket.IO聊天
*{边距:0;填充:0;框大小:边框框;}
正文{字体:13px Helvetica,Arial;}
表单{background:#000;填充:3px;位置:固定;底部:0;宽度:100%;}
表单输入{边框:0;填充:10px;宽度:90%;右边距:5%;}
窗体按钮{宽度:9%;背景:rgb(130224255);边框:无;填充:10px;}
#消息{列表样式类型:无;边距:0;填充:0;}
#消息li{padding:5px 10px;}
#留言李:第n个孩子(奇数){背景:#eee;}
$(函数(){
var socket=io.connect('https://10.0.4.18:8020“,{secure:true,port:8020,query:'PHP生成的用于从node.js对用户进行身份验证的令牌'});
//单击“发送”按钮时
$('#f')。单击(函数(e){
e、 预防默认值();
var message=$('#m').val().trim();
如果(消息==''){
返回false;
}
发出('chat',消息);
$('m').val('');
});
socket.on('chat',函数(msg){
$('#messages')。追加($('
  • ')。文本(msg.message)); }); });
      发送
    • 将令牌存储在浏览器中,并将其与来自php的每个请求一起发送,带有一个如下所示的授权承载器:

      Authorization: Bearer token
      
      因此,每当node.js服务器收到请求时,您都可以比较用户对应的令牌并获取数据

      您可以在表中存储userid和具有到期日期的令牌之间的关系,并在每次登录时刷新令牌

      另外,如果不想使用表,可以使用JWT将数据存储在令牌中

      您可以在此处找到更多信息:


      我认为这是不可能的,因为当页面被重新加载或浏览器移动到另一个页面时,所有JavaScript实例都会死亡,因为它们每页都是唯一的,需要建立一个新的套接字连接,如果您想从新连接中识别用户,最好的方法是使用会话,当用户首次登录或连接时,将创建一个唯一的会话id,并将其从服务器发送到用户,并存储在本地,因此每次客户端启动新连接时,它都会使用到服务器的会话id来标识自己,服务器应recoginze@DanielMendel如何将会话传递回客户端?从node.js服务器,在第一次请求之后,我如何加载特定的sessionID?我完全同意Alex7,事实上之前我使用socket.io节点加入了php会话,这对我来说很好,但我在服务器中的physic php会话上附加了很多耦合代码。。这是一个可伸缩性问题,另一个原因是。。如果您可以以例如Json格式编写未耦合的后端响应,那么您可以使用客户端性能来呈现视图,而不是使用服务器在每个请求中发送响应视图。。。它将提高性能,而且您可以更快地将其打包到应用程序中(如ionic、phonegap、react等)@Alex7谢谢您的反馈。如果我理解正确,我应该在node.js上有一个关系对象,它在sessionID和头中传递的令牌之间保持关系。然后在中间件中添加一个检查,如果令牌已经存在,则加载现有会话。如果没有,则生成一个新会话a