javascript dgram 节点中的 UDP pinger 超时

UDP pinger timeout in javascript dgram node

因此,对于我正在学习的课程,我们使用 Node.js 和 Dgram 在 Javascript 中编写 UDP pinger。我们被分配了以下任务:

为应用程序创建客户端代码。您的客户端应向目标 UDP 服务器发送 10 条 ping 消息。对于每条消息,您的客户端应计算从发送包裹到收到响应的往返时间。如果包裹在途中掉落,客户也要处理。这应该通过让客户端在发送每个包后等待 1 秒的响应来完成。如果没有收到回复,客户端应该进行相应的记录(丢包、无响应、超时等)并发送新的包重试。但是,发送的包总数应该仍然只有 10。客户端还应该计算收到的包 lost/no 响应的百分比,并在连接关闭之前记录它。

如果课程看起来相当简单,我也这么认为。我已经编写了一段时间的代码,快要完成了,但是我在让客户端发送包、等待响应然后采取相应行动方面遇到了问题。

到目前为止,我的代码所做的基本上是发送一个 ping,当收到一个 pong 时,它会发送另一个 ping。我想不通的是如何让它在发送下一个包裹之前记录没有收到回复。换句话说,我知道如何让它对收到的响应做出反应,我只是不知道如果在设定的时间范围内没有给出响应如何让它响应。我试过使用 if 语句和循环以及异步函数,但我还没有让它工作,所以现在我正在寻求帮助。

代码在这里:

const dgram = require("dgram");
const ms = require("ms"); 

var client = dgram.createSocket("udp4");


const PORT = 8000;
const HOST = "localhost";

let today = "";
let t0 = "";
let t1 = "";
let RTT = "";

let sentPackages = "";
let receivedPackages = "";

const messageOutbound = Buffer.from("You Up?");

sendPackage();

const x = setInterval(sendPackage, 1000);
client.on("message", (message, remote) => {
    receivedPackages++
    today = new Date();
    t1 = today.getTime();
    console.log(
      `Message from: ${remote.address}:${remote.port} saying: ${message}`
    );
    RTT = ms(t1 - t0, { long: true });
    console.log(RTT);
    const x = setInterval(sendPackage, 1000);
  });

client.on('error', (err) => {
  console.log(`server error:\n${err.stack}`);
  server.close();
});  

async function sendPackage() {
  if (sentPackages < 10) {
    client.send(messageOutbound, 0, messageOutbound.length, PORT, HOST, () => {
      sentPackages++
      let today = new Date();
      t0 = today.getTime();
      console.log(
        `message has been sent to ${HOST}:${PORT}. Message sent at: ${t0}`
      );
    });
  } else {
    calculateLoss();
    client.close();
  }
};
function calculateLoss() {
  let amountLost =  sentPackages - receivedPackages;
  let percentageLoss = amountLost / sentPackages * 100
  console.log(amountLost);
  console.log(percentageLoss +"% of packages lost");
};

我会使用 async / await 在消息之间简单地等待 1000 毫秒/1 秒,然后跟踪数组中的所有消息。

我们用 uuid 来识别消息,因此我们可以确保我们收到的消息与我们发送的消息相匹配。

之后我们可以记录所有需要的统计信息:

const dgram = require("dgram");
const uuid = require('uuid');

const PORT = 8000;
const HOST = "localhost";

const client = dgram.createSocket("udp4");

// Array that keeps track of the messages we send
let messages = [];

// When we get a message, decode it and update our message list accordingly...
client.on("message", (messageBuffer, remote) => {
    let receivedMessage = bufferToMessage(messageBuffer);
    // Find the message we sent and set the response time accordingly.
    let message = messages.find(message => message.uuid === (receivedMessage ||{}).uuid);
    if (message) {
        message.responseTimestamp = new Date().getTime();
    }
});

client.on('error', (err) => {
    console.log(`server error:\n${err.stack}`);
    server.close();
});  

function createMessage() {
    return { uuid: uuid.v4() };
}

function messageToBuffer(message) {
    return Buffer.from(JSON.stringify(message), "utf-8");
}

function bufferToMessage(buffer) {
    try {
        return JSON.parse(buffer.toString("utf-8"));
    } catch (error) {
        return null;
    }
}

// Wait for timeout milliseconds
function wait(timeout) {
    return new Promise(resolve => setTimeout(resolve, timeout));
}

function sendMessage(message, port, host) {
    // Save the messages to our list...
    messages.push(message);
    console.log(`Sending message #${messages.length}...`);
    // Set the time we send out message...
    message.sentTimestamp = new Date().getTime();
    let messageBuffer = messageToBuffer(message);
    return new Promise((resolve, reject) => {
        client.send(messageBuffer, 0, messageBuffer.length, port, host, (error, bytes) => {
            if (error) {
                reject(error);
            } else {
                resolve(bytes);
            }
        })
    });      
}

async function sendMessages(messageCount, port, host, timeout) {
    for(let messageIndex = 0; messageIndex < messageCount; messageIndex++) {
        let message = createMessage();
        await sendMessage(message, port, host);
        await wait(timeout);
        if (message.responseTimestamp) {
            console.log(`Response received after ${message.responseTimestamp - message.sentTimestamp} ms...`);
        } else {
            console.log(`No response received after ${timeout} ms...`);
        }
    }
    logStatistics(messages);
}

function logStatistics(messages) {
    let messagesSent = messages.length;
    let messagesReceived = messages.filter(m => m.responseTimestamp).length;
    let messagesLost = messagesSent - messagesReceived;
    console.log(`Total messages sent: ${messagesSent}`);
    console.log(`Total messages received: ${messagesReceived}`);
    console.log(`Total messages lost: ${messagesLost} / ${(100*messagesLost / (messages.length || 1) ).toFixed(2)}%`);
    if (messagesReceived > 0) {
        console.log(`Average response interval:`, messages.filter(m => m.responseTimestamp).reduce((averageTime, message) =>  {
            averageTime += (message.responseTimestamp - message.sentTimestamp) / messagesReceived;
            return averageTime;
        }, 0) + " ms");
    }
}

sendMessages(10, PORT, HOST, 1000);