You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UDP Pinger套接字开发:如何让服务端仅在收到客户端消息后回复

UDP Pinger问题修复与往返时间计算方案

核心问题原因

  • 服务端message事件内的for循环导致单次收到消息就回复10条,不符合请求-响应的逻辑
  • 时间戳全局初始化,所有请求复用同一个时间对象,导致打印时间全部相同
  • 客户端当前仅发送1次请求,未实现10次Ping的发送逻辑,也没有做收发的时序控制

修正后代码

服务端代码

const host = "127.0.0.1", port = 33333;
const dgram = require("dgram");
const server = dgram.createSocket("udp4");

server.on('listening', () => {
  const address = server.address();
  console.log(`server listening on port ${address.port} and address ${address.address}`);
});

server.on("message", function(msg, rinfo) {
  // 收到一次消息仅回复一次,实时生成当前时间戳
  const receiveTime = new Date();
  const resMessage = 'Pong';
  server.send(resMessage, 0, resMessage.length, rinfo.port, rinfo.address, (err) => {
    if(err) console.error('回复失败:', err);
  });
  console.log(`${rinfo.address}:${rinfo.port} - 收到${msg} - 回复时间: ${receiveTime.toISOString()}`);
});

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

server.on("close", function(){
  console.log("\n 服务端已关闭")
})

server.bind(port, host);

客户端代码

const host = "127.0.0.1", serverPort = 33333, clientPort = 33334;
const dgram = require("dgram");
const client = dgram.createSocket("udp4");

// 存储每次请求的时间,用于计算RTT
const pingTimes = [];
const rttResults = [];
let currentPingCount = 0;
const maxPingCount = 10;

// 单次发送Ping的函数
function sendPing() {
  if(currentPingCount >= maxPingCount) {
    // 全部请求完成后计算统计信息
    console.log('\n===== 10次Ping-Pong交互完成 =====');
    console.log('各次RTT(ms):', rttResults);
    const avgRtt = rttResults.reduce((a,b) => a + b, 0) / rttResults.length;
    const minRtt = Math.min(...rttResults);
    const maxRtt = Math.max(...rttResults);
    const lossRate = ((maxPingCount - rttResults.length)/maxPingCount * 100).toFixed(2);
    console.log(`平均RTT: ${avgRtt.toFixed(2)}ms, 最小RTT: ${minRtt}ms, 最大RTT: ${maxRtt}ms, 丢包率: ${lossRate}%`);
    client.close();
    return;
  }
  const sendTime = Date.now();
  pingTimes[currentPingCount] = sendTime;
  const message = `Ping ${currentPingCount + 1}`;
  client.send(message, 0, message.length, serverPort, host, (err) => {
    if(err) {
      console.error(`第${currentPingCount + 1}次Ping发送失败:`, err);
      currentPingCount++;
      // 发送失败也进入下一次,模拟超时
      setTimeout(sendPing, 1000);
    }
  });
  // 设置1秒超时,超时则认为丢包
  setTimeout(() => {
    if(!rttResults[currentPingCount]) {
      console.log(`第${currentPingCount + 1}次Ping请求超时`);
      currentPingCount++;
      sendPing();
    }
  }, 1000);
}

client.on('listening', () => {
  const address = client.address();
  console.log(`client listening on port ${address.port} and address ${address.address}`);
  // 启动后发送第一次Ping
  sendPing();
});

client.on("message", function(msg, rinfo) {
  const receiveTime = Date.now();
  const rtt = receiveTime - pingTimes[currentPingCount];
  rttResults[currentPingCount] = rtt;
  console.log(`${rinfo.address}:${rinfo.port} - 收到${msg} - 本次RTT: ${rtt}ms`);
  currentPingCount++;
  // 收到回复后发送下一次Ping
  sendPing();
});

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

client.on("close", function(){
  console.log("\n 客户端已离线")
})

client.bind(clientPort, host);

改动说明

  • 移除了服务端消息回调内的for循环,实现收一次请求回复一次响应的逻辑,避免批量发送
  • 时间戳改为每次收发消息时实时生成,保证每条消息的时间独立
  • 客户端实现了10次请求的时序控制:每次收到上一次的Pong回复后才发送下一次Ping,同时添加1秒超时判断模拟丢包场景
  • 内置了RTT计算逻辑,全部交互完成后会自动统计平均、最小、最大RTT以及丢包率

内容的提问来源于stack exchange,提问作者legend777

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:45:00