UNPKG

magic-packet

Version:

发送Wake-on-LAN魔术包唤醒局域网电脑

174 lines (149 loc) 5.46 kB
#!/usr/bin/env node const dgram = require('dgram'); const { program } = require('commander'); /** * 验证MAC地址格式 * @param {string} macAddress - MAC地址 (格式: XX:XX:XX:XX:XX:XX 或 XX-XX-XX-XX-XX-XX) * @returns {boolean} 是否是有效的MAC地址 */ function isValidMacAddress(macAddress) { const macRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; return macRegex.test(macAddress); } /** * 创建魔术包 * @param {string} macAddress - MAC地址 (格式: XX:XX:XX:XX:XX:XX 或 XX-XX-XX-XX-XX-XX) * @returns {Buffer} 魔术包字节 * @throws {Error} MAC地址格式错误时抛出异常 */ function createMagicPacket(macAddress) { // 验证MAC地址格式 if (!isValidMacAddress(macAddress)) { throw new Error('MAC地址格式错误,请使用格式 XX:XX:XX:XX:XX:XX 或 XX-XX-XX-XX-XX-XX'); } // 移除MAC地址中的分隔符 const mac = macAddress.replace(/[:-]/g, ''); // 创建魔术包: 6字节的0xFF,后面跟着目标MAC地址重复16次 const magic = Buffer.alloc(102); // 6 + 16*6 = 102 bytes // 填充前6个字节为0xFF magic.fill(0xFF, 0, 6); // 将MAC地址解析为字节并复制到缓冲区 const macBytes = []; for (let i = 0; i < 6; i++) { macBytes.push(parseInt(mac.substr(i * 2, 2), 16)); } // 重复16次MAC地址 for (let i = 0; i < 16; i++) { macBytes.forEach((byte, j) => { magic[6 + i * 6 + j] = byte; }); } return magic; } /** * 发送魔术包 * @param {string} macAddress - 目标计算机的MAC地址 * @param {string} [ipAddress='255.255.255.255'] - 广播地址 * @param {number} [port=9] - 目标端口 * @param {Object} [options={}] - 额外选项 * @param {number} [options.retries=0] - 失败时重试次数 * @param {boolean} [options.silent=false] - 是否静默模式(不输出日志) * @returns {Promise<void>} */ async function sendMagicPacket(macAddress, ipAddress = '255.255.255.255', port = 9, options = {}) { const { retries = 0, silent = false } = options; let attempts = 0; let lastError = null; // 尝试发送,如果失败则重试 while (attempts <= retries) { try { // 创建魔术包 const magicPacket = createMagicPacket(macAddress); // 发送数据包 await sendUdpPacket(magicPacket, ipAddress, port); if (!silent) { console.log(`魔术包已成功发送到 ${macAddress}`); } return; // 成功发送 } catch (err) { lastError = err; attempts++; if (attempts <= retries) { if (!silent) { console.log(`发送失败,正在尝试第 ${attempts} 次重试...`); } // 等待一段时间后重试 await new Promise(resolve => setTimeout(resolve, 500)); } } } // 所有尝试都失败 throw lastError || new Error('发送魔术包失败'); } /** * 发送UDP数据包 * @param {Buffer} packet - 要发送的数据包 * @param {string} ipAddress - 目标IP地址 * @param {number} port - 目标端口 * @returns {Promise<void>} * @private */ function sendUdpPacket(packet, ipAddress, port) { return new Promise((resolve, reject) => { // 创建UDP socket const socket = dgram.createSocket('udp4'); // 设置错误处理 socket.on('error', (err) => { socket.close(); reject(new Error(`UDP发送错误: ${err.message}`)); }); // 设置广播选项并发送数据包 socket.on('listening', () => { socket.setBroadcast(true); socket.send(packet, port, ipAddress, (err) => { socket.close(); if (err) { reject(new Error(`发送数据包失败: ${err.message}`)); } else { resolve(); } }); }); // 绑定到随机端口 socket.bind(); }); } // 如果直接运行此文件(而不是作为模块导入),则执行命令行功能 if (require.main === module) { // 设置命令行参数 program .name('magic-packet') .description('发送Wake-on-LAN魔术包唤醒局域网中的计算机') .version('1.1.0') .requiredOption('-m, --mac <address>', '目标计算机的MAC地址 (格式: XX:XX:XX:XX:XX:XX)') .option('-i, --ip <address>', '广播地址', '255.255.255.255') .option('-p, --port <number>', '目标端口', '9') .option('-r, --retries <number>', '发送失败时的重试次数', '0') .option('-s, --silent', '静默模式,不输出日志', false) .parse(process.argv); const options = program.opts(); // 发送魔术包 sendMagicPacket( options.mac, options.ip, parseInt(options.port), { retries: parseInt(options.retries), silent: options.silent } ).catch(err => { console.error(`错误: ${err.message}`); process.exit(1); }); } // 导出函数,使其可以作为模块使用 module.exports = { isValidMacAddress, createMagicPacket, sendMagicPacket };