通过 UDP 类返回对象数组

问题描述

我制作了一个节点服务器,它通过 udp 连接返回服务器响应。

服务器响应和 udp 连接是通过 serverQuery 类处理的。我的问题是没有用于 UDP 连接的 await 命令,当我从服务器获得响应时我没有。所有服务器响应都被推送到一个数组 (responseArr),然后该数组被发送到响应对象。但是,当我发送响应对象时,它总是作为一个空数组出现,因为在代码执行时服务器还没有响应。

非常感谢您对此的任何见解。另外,请随时对我的代码提供反馈,因为我还是编程新手,谢谢!

index.js:

const fs = require('fs');
const express = require('express');
const serverQuery = require('./serverQuery');

PORT = process.env.PORT || 3000;

var app = express();

app.get('/api/serverlist/getstatus/:port/:host',(req,res) => {
    requestObj = [
        {
            "host": req.params.host,"port": +req.params.port
        }
    ];
    let response = new serverQuery(requestObj,'getstatus',res);    
});

app.get('/api/serverlist',res) => {

    var data = fs.readFileSync('servers.json','utf8');
    var obj = JSON.parse(data);
    
    for (server in obj) {
        new serverQuery(host,port,res,true);
    }

    // new serverQuery(host,res);

});

app.get('/api/serverlist/getinfo/:port/:host','getinfo',res);
    res.send(response.responseArr);
    
});


app.listen(PORT,() => {
    log(`Server running on port: ${PORT}`);
});

function log (ref) {
    console.log(ref);
}

serverQuery.js

const Dgram = require('dgram');
const EventEmitter = require('events');


class ServerQuery extends EventEmitter {

    // PROBLEM: UDP has no await/async functionality.
    // How to return array of serverinfo when server response
    // is not instantaneous?

    // Test server: 35.194.92.249:29071

    #cmdPrefix = Buffer.from([0xFF,0xFF,0x02]);
    #cmd = '';

    constructor (servers,query,res) {

        super();
        this.responseStatus = {"error": 200,"description": "OK",host: "255.255.255.255",port: "55555"};
        this.responseArr = [];

        this.on('response',function (serverResponse,responseArr) { 
            responseArr.push(serverResponse);
            log(responseArr); // working
        });

        this.on('send',function (responseArr) {
            res.send(responseArr);
        });

        let isValidQuery = true;
        if (query === 'getinfo') {
            this.#cmd = Buffer.from('getinfo\n','binary');
        } else if (query === 'getstatus') {
            this.#cmd = Buffer.from('getstatus\n','binary');
        } else {
            let errResponse = this.getError('invalid_query');
            this.responseArr.push(errResponse);
            log(errResponse);
            res.send(this.responseArr);
            isValidQuery = false;
        }

        if (isValidQuery) {   
            for (let server of servers) {
                let validatePort = this.validate(server.port,'port');
                let validateHost = this.validate(server.host,'ip');
    
                if (validatePort.isValid && validateHost.isValid) {
                    this.newUDPConnection(server.host,server.port,this.responseArr);
                } else {
                    this.responseArr.push(this.getError('invalid_address',server.host,server.port));
                }
            }
    
            log(this.responseArr); // is getting executed before response emit
            this.emit('send',this.responseArr);
        }
    }

    newUDPConnection (host,responseArr) {
        const udp = Dgram.createSocket('udp4');
        var closeUDP = setTimeout(() => {
            udp.close();
            this.emit('response',this.getError('server_timeout',host,port),responseArr);
        },1000);

        udp.on('message',(msg,rinfo) => {
            let serverResponse = this.statusResponseHandler(msg,rinfo);
            this.emit('response',serverResponse,responseArr);
            udp.close();
            clearTimeout(closeUDP);
        })

        udp.send([this.#cmdPrefix,this.#cmd],(err) => {
            if (err) {
                log(err);
                udp.close();
            } else {
                log(`Query: ${this.#cmdPrefix.toString() + this.#cmd.toString()}Address: ${host}:${port}`);
            }
        });
    }


    validate(refVar,varType) {
        let isValid = false;
        let status = "INVALID ParaMETERS";

        if (varType != 'port' && varType != 'ip') {
            console.log('INVALID ParaMETERS');
            return;
        }

        if (varType == 'port') {
            if (+refVar > 0 && +refVar < 65536) {
                isValid = true;
                status = "OK";
            } else {
                status = "INVALID";
            } 

        }

        if (varType == 'ip') {
            if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(refVar)) {
                isValid = true;
                status = "OK";
            } else {
                status = "INVALID";
            }
        }
        return {'isValid': isValid,'status': status};
    }

    statusResponseHandler (msg,rinfo) {
        let returnObj = {};

        // server address
        const address = {"host": rinfo.address,"port": rinfo.port};
        returnObj.address = address;

        msg = msg.toString();

        // remove colors
        msg = msg.replace(/\^[0-9]/g,'');

        // remove quotes
        msg = msg.replace(/\"/g,'');

        // remove special chars
        msg = msg.replace(/[^\x00-\x7F]+/g,'');

        // remove the '????statusResponse' from the server response
        let msgArr = msg.slice(msg.indexOf('\\') + 1,msg.length).split("\\");

        // convert array to an object with key value pairs
        msgArr.forEach(function (a,i,aa) {
            if (i & 1) {
                returnObj[aa[i - 1]] = a;
            }
        });

        // remove player info from the last key-value pair in the return object
        let lastKey = Object.keys(returnObj)[Object.keys(returnObj).length-1];
        let lastValue = returnObj[Object.keys(returnObj)[Object.keys(returnObj).length-1]];

        returnObj[lastKey] = lastValue.slice(0,lastValue.indexOf('\n'));

        // generate player info object and push to return object 
        returnObj.players = [];
        let playeRSStr = lastValue.slice(lastValue.indexOf('\n') + 1,lastValue.length);
        let playersArr = playeRSStr.split('\n');

        playersArr.forEach(player => {
            let playerInfo = player.split(' ',3);

            if (!playerInfo[1]) {
            // do nothing - this is to ignore empty player strings
            } else {
                returnObj.players.push({
                    "ping": playerInfo[1],"score": playerInfo[0],"name": playerInfo[2],"isBot": (+playerInfo[1] === 0) ? true : false
                });
            }
        });
        return returnObj;
    }

    createResponseStatus (err,desc,port) {
        return {
            "error": err,"desc": desc,"host": host,"port": port
        }
    }

    getError (err,port) {
        let errorObj = {
            'invalid_address': this.createResponseStatus(422,`Invalid address`,'invalid_query': this.createResponseStatus(422,`Invalid query in serverQuery class constructor. Valid queries: \'getinfo\',\'getstatus\'`,'server_timeout': this.createResponseStatus(408,`Server timed out`,}
        return errorObj[err];
    }
}

function log (ref) {
    console.log(ref);
}

module.exports = ServerQuery;

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)