javascript - 数组在拼接后不会改变它的数据。范围问题?

标签 javascript arrays node.js

我有一个 tcp 服务器,我将所有客户端数据存储在 clients 数组中。 因此,当有人断开连接时,我只是拼接它们。

clients.splice(clients.indexOf(socket), 1);

我有一个显示所有在线用户的功能。它只是从客户端数组中获取数据并将其显示给我。

function showOnline(){
    console.log("Show user list");
    console.log("clients.length = " + clients.length);
}

clients 数组在 app.js 文件的顶部声明

var clients = []; <- here it is

var http = require('http');
var net = require('net');

所以现在当客户端断开连接时,我拼接他并收到这样的日志。

console.log("clients.indexOf(socket) = " + clients.indexOf(socket));
console.log("clients.length = " + clients.length);
clients.splice(clients.indexOf(socket), 1);
console.log("after splice clients.length = " + clients.length);

LOG
2016-03-04 22:06:33
clients.length = 2
after splice clients.length = 1

所以在客户端数组中只有 1 个用户。然后我使用我的显示在线用户功能,它给了我这样的日志

LOG
2016-03-04 22:06:41 - Show user list
clients.length = 2

显示有 2 个用户。怎么可能? 那是范围问题吗?或者它可以是什么?感谢您的任何回复。

来源。 我想我对套接字做错了什么。此代码有效,但它运行的时间越长,我在客户端数组中留下的幻影客户端就越多。

var clients = [];
var packetReg = /(ART\D\d{4}(.*?)\DAND)/g;

var serverUrl = "http://localhost:4000/"

// Exit packet
var p = {
    exit: true
};
var ePacket = 'ART|' + randomNumber(1000, 9000) + JSON.stringify(p) + '|AND';

var fs = require('fs');
var http = require('http');
var https = require('https');
var net = require('net');
var url = require('url');

http.createServer(function (req, res) {
    var queryData = url.parse(req.url, true).query;
    res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});

    if (queryData.clean) {
        kick();
    } else if (queryData.expel) {
        kick();
    } else if (queryData.list) {
        showUserList();
    }
    else {
        res.writeHead(302, {'Location': 'https://localhost'});
        res.end();
    }

    function kick(all){
        if (queryData.expel){
            LOG("Expel user " + queryData.expel + " cmd");
        }
        else LOG("Kick all cmd");
        clients.forEach(function (client) {
            if (queryData.expel){
                if (client.key != queryData.expel) return;
            }
            client.write(ePacket);
        });
        if (queryData.expel){
            res.end("done");
        }
        else {
            cleanOnline();
            res.end("disconnected - " + clients.length);
        }
    }

    function showUserList(){
        LOG("Show user list");
        var temp = 'Clients: ' + clients.length + generateButton('?clean=1', 'Kick all') + '<br>';
        clients.forEach(function (client) {
            temp += 'User: ' + client.name + ' Key: ' + client.key + generateButton('?expel=' + client.key, 'Kick') + '<br>';
        });
        console.log("clients.length = " + clients.length);
        res.end(temp);
    }

    function generateButton(link, text){
        return ' <a href="' + serverUrl + link + '" target="_blank" onClick="window.location.reload()"><button>' + text + '</button></a> ';
    }
}).listen(4000, function(){
     console.log('listening http on port 4000');
});

var tcpSocket = net.createServer();
tcpSocket.on('connection', function (socket) {
    socket.setNoDelay(true);
    socket.banned = false;
    socket.isAdmin = false;
    socket.isModerator = false;
    socket.name = 'newUser'
    socket.key = ''
    socket.armbuf = '';

    clients.push(socket);

    LOG("New connection #" + clients.length);

    // Exit packet that close opened software
    socket.on('doExit', function () {
        LOG("Send exit packet");
        var p = {
            exit: true
        };
        socket.write('ART|' + randomNumber(1000, 9000) + JSON.stringify(p) + '|AND');
        socket.destroy();
    });
    // Init packet 
    socket.on('init', function (newData) {
        LOG("Init packet");

        //newData.data = packet with key from my program
        //I find out if users start my program few times then they will be added few times at clients array. 
        //So i want to prevent that with that function. It finds sockets with same key and just disconnect them except for the last socket.
        //
        FindAndCleanDuplicateSockets(newData.data, socket);

        var tempSocket = findSocket(socket);
        tempSocket.socket.key = newData.data;

        LOG("Send request to localhost about key " + tempSocket.socket.key);
        https.get('https://localhost/tgo/api/?apiRequest&key=' + tempSocket.socket.key, (res) => {
            var initData = '';
            res.on('data', function (chunk) {
                initData += chunk;
            });
            res.on('end', function() {
                LOG("Receive answer from localhost - " + initData.toString());
                var a = JSON.parse(initData.toString());
                if (a.data = "OK"){
                    tempSocket.socket.name = a.name;
                    tempSocket.socket.banned = !Boolean(a.chatBan);
                    if (a.type == "admin")
                        tempSocket.socket.isAdmin = true;
                    else if (a.type == "moderator")
                        tempSocket.socket.isModerator = true;

                    updateSocket(tempSocket);
                    broadcast(packetMaker(tempSocket.socket.name, 'start'), socket);
                }
                else socket.emit('doExit');
            });
        }).on('error', (e) => {
            socket.emit('doExit');
        });
    });

    //When user send ping packet
    socket.on('ping', function (newData) {
        //LOG("Ping packet");
        var p = {
            currentTime: currentTime()
        };
        socket.write('ART|' + randomNumber(1000, 9000) + JSON.stringify(p) + '|AND');
    });

    // When user change his name
    socket.on('newName', function (newData) {
        LOG("User change name from " + socket.name + " to " + newData.data);

        var tempSocket = findSocket(socket);
        tempSocket.socket.name = newData.data;
        updateSocket(tempSocket);
    });

    //When user send new message packet
    socket.on('newMsg', function (newData) {
        LOG("newMsg packet");
        var tempSocket = findSocket(socket);

        if (tempSocket.socket.banned) {
            LOG('User ' + tempSocket.socket.key + ' chat is banned');
            return;
        }

        var type = 'msg';
        if (tempSocket.socket.isAdmin)
            type = 'admin';
        else if (tempSocket.socket.isModerator)
            type = 'moderator';

        broadcast(packetMaker(tempSocket.socket.name, type, String(newData.data)), socket);
    });

    // Handle incoming messages from clients.
    socket.on('data', function (newData) {
        var d = String(newData);
        //LOG('Received data: ' + d);

        // I find that socket bacause i use buffer to store data. If i won't do that it will give me "is not defined" error
        var tempSocket = findSocket(socket);
        tempSocket.socket.armbuf += d;

        var dataArray = tempSocket.socket.armbuf.match(packetReg);
        if (dataArray != null && dataArray.length > 0){
            dataArray.forEach(function (match) {
                tempSocket.socket.armbuf = tempSocket.socket.armbuf.replace(match, "");
                if (match.startsWith('ART|') && match.endsWith('|AND')) {
                    var j = JSON.parse(cleanPacket(match));
                    switch (j.type) {
                        case 'init':
                          socket.emit('init', j);
                          break;
                        case 'ping':
                          socket.emit('ping', j);
                          break;
                        case 'newName':
                          socket.emit('newName', j);
                          break;
                        case 'newMsg':
                          socket.emit('newMsg', j);
                          break;
                        default:
                          break;
                    }
                }
                else console.log('Bad packet: ' + match);
                //LOG("armbuf.length = " + tempSocket.socket.armbuf.length);
            });
        }
        updateSocket(tempSocket);
    });

    socket.on('error',function(error) {
        socket.end();
    });

    socket.on('close',function() {
        var tempSocket = findSocket(socket);

        LOG("Send logout notification to localhost - " + tempSocket.socket.key);
        // Send info to api that user logout
        https.get('https://localhost/tgo/api/?logout&key=' + tempSocket.socket.key);
        // Broadcast data to all users that client is logout
        broadcast(packetMaker(tempSocket.socket.name, 'exit'), socket);

        console.log("clients.indexOf(socket) = " + clients.indexOf(socket));
        console.log("clients.length = " + clients.length);
        // Delete user from clients array
        clients.splice(clients.indexOf(socket), 1);
        console.log("after splice clients.length = " + clients.length);
        LOG("Close from API - " + tempSocket.socket.key);
        socket.destroy();
    });


    function cleanPacket(packet){
        packet = packet.replace("ART|", "");
        packet = packet.replace("|AND", "");
        return packet.substring(4);
    }
    function findSocket(socket){
        var socketData = {
            'id': clients.indexOf(socket),
            'socket': clients[clients.indexOf(socket)]
        };
        return socketData;
    }
    function FindAndCleanDuplicateSockets(key, exclude){
        clients.forEach(function (client) {
            if (client == exclude && client.key != key) return;

            LOG("User already exist in array. Delete old socket");
            client.emit('doExit');
        });
    }

    function findAllSocketsByKey(key, excludeSocket){
        var sockets = [];
        clients.forEach(function (client) {
            if (client == excludeSocket && client.key != key) return;
            sockets.push(client);
        });
        return sockets;
    }

    function updateSocket(tempSocket){
        clients[tempSocket.id] = tempSocket.socket;
    }

     // Send a message to all clients
    function broadcast(message, sender) {
        if (clients.length <= 0) return;

        LOG('broadcast ' + message);
        clients.forEach(function (client) {
            // Don't want to send it to sender
            if (client === sender) return;
            client.write(message);
        });
    }

    function packetMaker(userName, packetType, userMsg){
        var p = {
            currentTime: currentTime(),
            chat: {
                user: userName,
                type: packetType
            }
        };
        if (typeof userMsg != 'undefined')
            p['chat']['msg'] = userMsg;
        return 'ART|' + randomNumber(1000, 9000) + JSON.stringify(p) + '|AND';
    }
});


tcpSocket.listen(5000, function(){
     console.log('listening tcpSocket on port 5000');

     cleanOnline();
});

function cleanOnline(){
    console.log('Clean DB online');
    //https.get('https://localhost/tgo/api/?apiCleanOnline');
}

function LOG(data){
    console.log(currentTime() + " - " + data);
}

function randomNumber(min, max)
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function currentTime() {
    var date = new Date();

    var hour = date.getHours();
    hour = (hour < 10 ? "0" : "") + hour;

    var min  = date.getMinutes();
    min = (min < 10 ? "0" : "") + min;

    var sec  = date.getSeconds();
    sec = (sec < 10 ? "0" : "") + sec;

    var year = date.getFullYear();

    var month = date.getMonth() + 1;
    month = (month < 10 ? "0" : "") + month;

    var day  = date.getDate();
    day = (day < 10 ? "0" : "") + day;

    return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}

最佳答案

这是你的罪魁祸首:

function findSocket(socket){
    var socketData = {
        'id': clients.indexOf(socket),
        'socket': clients[clients.indexOf(socket)]
    };
    return socketData;
}

我看到您使用此函数获取数组中的套接字索引(由属性 id 引用)以备后用。

问题来了。

想象一下您应用中的这一系列事件:

此时你有 2 个连接。

  1. 现在接收新连接 您刚刚将第三个套接字插入客户端数组 tempSocket 现在的 id 为 2

  2. 应用调用 api 获取 key

  3. 现有套接字接收断开连接事件 该应用程序从客户端数组中删除现有套接字,您只剩下 2 个套接字。它有两个,因为我们已经在项目符号 #1 中添加了新套接字

  4. 项目符号 #2 中的 api 调用返回 key 现在您调用 updateStocket(tempSocket) 来更新它。 问题是 tempSocket.id 指向索引 2,但数组中只有 2 个元素。

我的建议:

我。将客户端数组更改为对象集合 二.当您获得新的套接字连接时,使用像 UUID.js 这样的库来获得一个唯一的十六进制并将其分配给新的套接字属性 三.使用十六进制 ID 向客户端对象添加新套接字

如果您决定进行上述更改,则必须更新迭代方法。我建议使用 lodash 来帮助您迭代数组和对象。真的很方便。否则,您必须获取对象键并遍历它们。

祝你好运。

关于javascript - 数组在拼接后不会改变它的数据。范围问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35804719/

相关文章:

Javascript 在数组中存储 <select> 下拉值

java - 如何将 ExpandableListView 的选定项目存储在 ArrayList 中

python - _ZSt28__throw_bad_array_new_lengthv 尝试在编译共享对象后在 Python 中导入 C++ 代码

css - 根据文本方向属性提供不同的 SCSS 文件

javascript - 在 "on"语句之外定义套接字 io 函数

javascript - 如何在网页上显示动态<img src>

javascript - 在类项目上注册事件并在单击时获取其属性

javascript, gulp, watch, 改变了

C - 将 char 指针传递给函数的段错误

javascript - 无法修改javascript回调中的值