关于WebSocket-Node模块的Session改造[Nodejs版 更新0.2]

关于WebSocket-Node模块的Session改造[Nodejs版 更新0.2]

整条知识链已建立好,剩下就是慢慢把整体描画完整,一边补充知识点了; 课题是建立一个自己的框架,暂定目标是基于WebSocket的Web2.0 SPA,Ajax只会作为补充,自己玩就不考虑用户了; 今天把WebSocket-Node改造了一下,让它认得哪个用户发送信息,回复的时候只回复给源头; 我用了一个Child_process处理数据库的工作,尽量折腾一点,也可以合理分布资源; 接下来先简单说一下WebSocket-Node的设计思路:

1,建立wsServer实例时,在http服务器挂上’upgrade’事件,每一个用户连接进来upgrade的时候,调用handleUpgrade方法 2,handleUpgrade方法会建立wsRequest实例,然后调用实例的handShake()方法,通过后emit在App.js写的’request’事件 3,触发request事件后,调用wsRequest.accept()构造函数,过程建立新的wsConnection对象并返回之,返回前触发’requestAccepted’事件 4,'requestAccepted’事件会把新建立的wsConnection对象传入wsServer的connections数组!

所以,有多少个用户连接进来,wsServer的connections数组就有多少个元素,用户连接断开后删除对应的数组元素;

改造思路: 因为是child_process处理数据库,简单的只在其’message’事件挂上connection.sendUTF并不合理,因为一个message会触发所有用户的’message’事件,结果就是每一个用户都接收到相同的信息;如果想用数组操作每一个wsConnection实例,就要用indexOF遍历,因为每一次用户进出,都会改变其他用户的实例的位置:

  WebSocketServer.prototype.handleRequestAccepted = function(connection) {
    var self = this;
    connection.once('close', function(closeReason, description) {
        self.handleConnectionClose(connection, closeReason, description);
    });
    this.connections.push(connection);
    this.emit('connect', connection);
};
WebSocketServer.prototype.handleConnectionClose = function(connection, closeReason, description) {
    var index = this.connections.indexOf(connection);
    if (index !== -1) {
        this.connections.splice(index, 1);
    }
    this.emit('close', connection, closeReason, description);

我暂时的想法是用对象代替数组,因为cookie那边还没想好和浏览器的约定,我先用随机数做用户的session,用connections[random]来储存用户session

WebSocketServer.prototype.handleRequestAccepted = function(connection) {
    var self = this;
    var randomName=Math.floor(Math.random() * 1000000);
    var checkClientNumber = function(){
        if(self.connections[randomName]){
            randomName = Math.floor(Math.random() * 1000000);
            checkClientNumber();
        }
        return;
    }
    checkClientNumber();
    connection.once('close', function(closeReason, description) {
        self.handleConnectionClose(connection, closeReason, description);
    });
    connection.clientNumber=randomName;
    this.connections[randomName]=connection;
    this.emit('connect', connection);
};

WebSocketServer.prototype.handleConnectionClose = function(connection, closeReason, description) { delete this.connections[connection.clientNumber]; this.emit(‘close’, connection, closeReason, description); };

这样改造好,每一个wsConnection实例都会有自己的clientNumber,在用户连接成功后直接发送给用户,浏览器端保存进ws对象; 而和child_process交流的时候,都用一个对象交流,对象其中一个属性为clientNumber,并和child返回的对象一并发回; 把wsServer的’request’事件多传入一个this参数,让callback能访问这个实例,最后就可以调用每一个对应用户的wsConnection实例了:

wsServer.on('request', function(wsRequest,wsServer){
    var connection = wsRequest.accept('big-protocol', wsRequest.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.sendUTF(connection.clientNumber);

    dbChild.on('message', function(msg){
        wsServer.connections[msg.cNum].sendUTF(msg.msg);
    });
//.................etc

除了说思路,还想请教一下,这样操作一个巨大的对象如wsServer.connections[msg.cNum]会不会造成性能问题?

我的想法是总比操作数组好吧?

上面的’request’事件写错了

上面这样写会导致每一个用户进来都声明一次’message’事件,有一亿个用户进来,就会回复一亿次重复的信息…低级错误了…

dbChlid.on应该在wsServer.on外面

var wsServer = new WebSocketServer({
    httpServer: server,
    autoAcceptConnections: false
});
dbChild.on('message', function(msg){
    wsServer.connections[msg.cNum].sendUTF(msg.msg);
});
wsServer.on('request', function(wsRequest, wsServer){
  //....etc

3 回复

关于WebSocket-Node模块的Session改造[Nodejs版 更新0.2]

整条知识链已建立好,剩下就是慢慢把整体描画完整,一边补充知识点了。课题是建立一个自己的框架,暂定目标是基于WebSocket的Web2.0 SPA,Ajax只会作为补充,自己玩就不考虑用户了。

今天把WebSocket-Node改造了一下,让它认得哪个用户发送信息,回复的时候只回复给源头。我用了一个child_process处理数据库的工作,尽量折腾一点,也可以合理分布资源。

WebSocket-Node的设计思路

  1. 建立wsServer实例

    • 在HTTP服务器挂上'upgrade'事件,每个用户连接进来时调用handleUpgrade方法。
  2. handleUpgrade方法

    • 建立wsRequest实例,然后调用实例的handShake()方法,通过后触发在App.js中定义的'request'事件。
  3. 触发request事件

    • 调用wsRequest.accept()构造函数,过程中建立新的wsConnection对象并返回之。返回前触发'requestAccepted'事件。
  4. 'requestAccepted'事件

    • 把新建立的wsConnection对象传入wsServerconnections数组。
// 示例代码
WebSocketServer.prototype.handleRequestAccepted = function(connection) {
    var self = this;
    connection.once('close', function(closeReason, description) {
        self.handleConnectionClose(connection, closeReason, description);
    });
    this.connections.push(connection);
    this.emit('connect', connection);
};

WebSocketServer.prototype.handleConnectionClose = function(connection, closeReason, description) {
    var index = this.connections.indexOf(connection);
    if (index !== -1) {
        this.connections.splice(index, 1);
    }
    this.emit('close', connection, closeReason, description);
};

改造思路

由于使用child_process处理数据库,简单的在'message'事件上挂上connection.sendUTF并不合理,因为一个消息会触发所有用户的'message'事件,导致每个用户都接收到相同的信息。如果想用数组操作每一个wsConnection实例,需要遍历数组,因为每次用户进出都会改变其他用户的实例位置。

为了改进这一点,可以使用对象代替数组。通过随机数生成用户的session,用connections[random]来存储用户session

WebSocketServer.prototype.handleRequestAccepted = function(connection) {
    var self = this;
    var randomName = Math.floor(Math.random() * 1000000);
    var checkClientNumber = function() {
        if (self.connections[randomName]) {
            randomName = Math.floor(Math.random() * 1000000);
            checkClientNumber();
        }
        return;
    }
    checkClientNumber();

    connection.once('close', function(closeReason, description) {
        self.handleConnectionClose(connection, closeReason, description);
    });

    connection.clientNumber = randomName;
    this.connections[randomName] = connection;
    this.emit('connect', connection);
};

WebSocketServer.prototype.handleConnectionClose = function(connection, closeReason, description) {
    delete this.connections[connection.clientNumber];
    this.emit('close', connection, closeReason, description);
};

优化后的代码

确保dbChild.on('message')事件只声明一次,避免重复事件触发:

var wsServer = new WebSocketServer({
    httpServer: server,
    autoAcceptConnections: false
});

dbChild.on('message', function(msg) {
    wsServer.connections[msg.cNum].sendUTF(msg.msg);
});

wsServer.on('request', function(wsRequest, wsServer) {
    var connection = wsRequest.accept('big-protocol', wsRequest.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.sendUTF(connection.clientNumber);

    // 其他逻辑...
});

性能考量

使用对象来存储连接可能会减少数组操作带来的性能问题。但是,对于非常大的连接数量,仍然需要注意内存和性能。建议根据实际情况进行测试和优化。


为什么websocket-node到现在还没有加入session,这样我就没办法处理单聊啊,求指导

在这个帖子中,作者讨论了如何使用 WebSocket-Node 模块改造 session 管理,以便能够区分不同用户并仅向源用户发送消息。作者提出了一种通过使用对象而非数组来存储连接的方法,从而更高效地管理和查找特定的连接。

改造思路

  1. 使用对象存储连接

    • 每个连接分配一个随机的 clientNumber
    • 使用该 clientNumber 作为键,将连接存储在 connections 对象中。
  2. 处理连接和关闭

    • 在连接成功时,生成一个唯一的 clientNumber 并将其分配给连接对象。
    • 将连接添加到 connections 对象中。
    • 当连接关闭时,从 connections 对象中删除相应的连接。

示例代码

const WebSocketServer = require('websocket').server;
const http = require('http');

const server = http.createServer();
server.listen(8080);

const wsServer = new WebSocketServer({ httpServer: server });

let connections = {};

wsServer.on('request', function(request) {
    const connection = request.accept('my-protocol', request.origin);
    const clientNumber = Math.floor(Math.random() * 1000000);
    
    connections[clientNumber] = connection;

    connection.on('close', function(reasonCode, description) {
        delete connections[clientNumber];
    });

    console.log((new Date()) + ' Connection accepted. Client Number: ' + clientNumber);
});

// 处理来自 child_process 的消息
const dbChild = require('./dbChild');
dbChild.on('message', function(msg) {
    if (connections[msg.cNum]) {
        connections[msg.cNum].sendUTF(msg.msg);
    }
});

性能考虑

  • 使用对象存储连接可以避免遍历数组的性能开销,尤其是在用户数量较多时。
  • 但是需要注意,频繁地生成和删除对象可能会增加垃圾回收的压力。

通过这种方式,可以有效地管理每个连接,并确保消息只发送给正确的用户。

回到顶部