Skip to main content

网络延迟测试Demo

使用tcp和ws模拟ping 64k数据包测延迟,目的是快速判断网络延迟和带宽

tcp模拟需要单独使用一个端口,集成express项目中没有成功

ws和http共享一个端口,经过测试没有问题,故使用ws模拟延迟测试

tcp测试网络延迟

  • 服务端
import net from 'net';

const PORT = 3000;
const SERVER_PACKET_SIZE = 64*1024;

const server = net.createServer((socket) => {
socket.on('data', (data) => {
// 收到数据后回发同样大小的数据
socket.write(data);
});
});

server.listen(PORT, () => {
console.log(`TCP Echo Server listening on port ${PORT}`);
});
  • 客户端
import net from 'net';

const SERVER_IP = '192.168.1.60';
const PORT = 3000;
const PACKET_SIZE = 64*1024;

function testTcpPing(ip, port, packetSize) {
return new Promise((resolve) => {
const client = new net.Socket();
const payload = Buffer.alloc(packetSize, 'A');
const start = Date.now();

client.connect(port, ip, () => {
client.write(payload);
});

let receivedBytes = 0;
client.on('data', (data) => {
receivedBytes += data.length;
if (receivedBytes >= packetSize) {
const latency = Date.now() - start;
client.destroy();
resolve(latency);
}
});

client.on('error', () => resolve(null));
});
}

(async () => {
const latency = await testTcpPing(SERVER_IP, PORT, PACKET_SIZE);
console.log(`TCP Ping 模拟延迟: ${latency} ms`);
})();

ws测试网络延迟

带jwt认证

  • 服务端

server.js

import express from 'express';
import { createServer } from 'http';
import { startWsEchoServer } from './wsEchoServer.js';

const app = express();
app.use(express.json());

// 注册原有路由

const PORT = process.env.PORT || 3000;
const httpServer = createServer(app);

// 启动 WebSocket Echo
startWsEchoServer(httpServer);

httpServer.listen(PORT, async () => {
try {
if (process.env.NODE_ENV === 'development') await sequelize.sync();
console.log(`🚀 HTTP + WebSocket 服务器启动: http://localhost:${PORT}`);
} catch (err) {
console.error('启动失败:', err);
}
});

wsAuth.js

// src/utils/wsAuth.js
import { jwtVerify } from 'jose';

/**
* WebSocket 认证封装
* @param {import('http').IncomingMessage} req
* @returns {Promise<object>} 返回用户 payload
*/
export async function wsAuth(req) {
// 获取 token
const url = new URL(req.url, `http://${req.headers.host}`);
let token = url.searchParams.get('token') || req.headers['sec-websocket-protocol']?.split(' ')[0];
// console.log(token)
if (!token) throw new Error('未提供 Token');

try {
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret);
return payload; // 返回用户信息
} catch (err) {
throw new Error('Token 无效或已过期');
}
}

wsEchoServer.js

import { WebSocketServer } from 'ws';
import { wsAuth } from './wsAuth.js';

export function startWsEchoServer(httpServer) {
const wss = new WebSocketServer({ server: httpServer, path: '/ws-echo' });

wss.on('connection', async (ws, req) => {
try {
const user = await wsAuth(req); // 调用封装方法
ws.user = user; // 保存用户信息
} catch (err) {
ws.close(4001, err.message);
return;
}

// 认证通过,开始回显
ws.on('message', (message) => {
ws.send(message);
});

ws.on('close', () => {});
ws.on('error', () => {});
});

console.log('✅ WebSocket Echo 已挂载在 /ws-echo (需要认证)');

return wss;
}

.env

JWT_SECRET=your_jwt_secret
  • 客户端
import WebSocket from 'ws';

const SERVER_URL = 'ws://192.168.1.60:3000/ws-echo';
const PACKET_SIZE = 64 * 1024;
const INTERVAL_MS = 2000;

// 你的 JWT token
const TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImFkbWluIiwidXNlcm5hbWUiOiJhZG1pbiIsImlhdCI6MTc2MDYyNTMwNCwiZXhwIjoxNzYwNjMyNTA0fQ.SIK3haqf50lSWujw3ylhU5BdHsxQhXdWFVDt-PaYDic'
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}

async function runLoop() {
while (true) {
// 在 URL 上附加 token
const ws = new WebSocket(`${SERVER_URL}?token=${TOKEN}`);

await new Promise((resolve, reject) => {
ws.on('open', resolve);
ws.on('error', reject);
}).catch(err => {
console.log('❌ WebSocket连接失败:', err.message);
return sleep(INTERVAL_MS);
});

const payload = Buffer.alloc(PACKET_SIZE, 'A');
const start = Date.now();

ws.on('message', (data) => {
const latency = Date.now() - start;
console.log(`📶 WebSocket延迟: ${latency} ms`);
ws.close();
});

ws.on('error', (err) => {
console.log('❌ WebSocket测试失败:', err.message);
ws.close();
});

// 发送数据
ws.send(payload);

await sleep(INTERVAL_MS);
}
}

runLoop();