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
相关产品推荐
相关产品推荐

