【含MV】获取授时中心时间
研究了一下 NTP 授时服务,MV/MZ 基于 nw.js 可以直接用。UDP 协议丢包率挺高,真实场景里估计需要一些重试机制。
核心脚本来自 GitHub:https://github.com/luk3skyw4lker/node-ntp-sync
用法:(因为不是新手向,所以没有做 RM 封装,自行取用 response 的值)
const NTP = window.NtpClient;
const client = new NTP('ntp.ntsc.ac.cn',
123,
{ timeout: 3000 });
client
.syncTime()
.then(response => console.log('TIME:', response))
.catch(console.log);
脚本:
(function(){
const udp = require('dgram');
function toMsecs(buffer, offset) {
let seconds = 0;
let fraction = 0;
for (let i = 0; i < 4; ++i) {
seconds = seconds * 256 + buffer;
}
for (let i = 4; i < 8; ++i) {
fraction = fraction * 256 + buffer;
}
return seconds + fraction / Math.pow(2, 32);
}
const toFrac = ts => {
return Math.floor(Math.abs(ts - Math.floor(ts)) * Math.pow(2, 32));
};
const sysToNTP = timestamp => timestamp + NTPPacket.NTP_DELTA;
const writeInMillis = (buffer, offset, ts, addDelta) => {
const seconds = addDelta ? ts + NTP_DELTA : ts;
const fraction = toFrac(ts);
// seconds
buffer = (seconds & 0xff000000) >> 24;
buffer = (seconds & 0x00ff0000) >> 16;
buffer = (seconds & 0x0000ff00) >> 8;
buffer = seconds & 0x000000ff;
// fraction
buffer = (fraction & 0xff000000) >> 24;
buffer = (fraction & 0x00ff0000) >> 16;
buffer = (fraction & 0x0000ff00) >> 8;
buffer = fraction & 0x000000ff;
return buffer;
};
const MODES = {
CLIENT: 3,
SERVER: 4
};
const NTP_DELTA = 2208988800;
class NTPPacket {
constructor(mode) {
Object.assign(this, {
mode: mode || 4,
leap: 0,
version: 3,
stratum: 0,
poll: 0,
precision: 0,
rootDelay: 0,
rootDispersion: 0,
referenceId: 0,
referenceTimestamp: 0,
originateTimestamp: 0,
rxTimestamp: 0,
txTimestamp: 0
});
}
static parse(data) {
if (data.length < 48) {
throw new Error('Invalid NTP Package');
}
const packet = new NTPPacket(4);
// Control bytes
packet.leap = (data >> 6) & 0x3;
packet.version = (data >> 3) & 0x7;
packet.mode = data & 0x7;
packet.stratum = parseInt(data) || 2;
packet.poll = parseInt(data) || 10;
packet.precision = parseInt(data);
packet.rootDelay = data.slice(4, 8).readFloatBE(0) / 2 ** 16;
packet.rootDispersion = data.slice(8, 12).readFloatBE(0) / 2 ** 16;
packet.referenceId = data.slice(12, 16);
// Timestamps where the 4 first bytes are the
// int part and the 4 last are the frac part
// const refTimestampHigh = data.slice(16, 20).readUint32BE();
// const refTimestampLow = data.slice(20, 24).readFloatBE();
packet.referenceTimestamp = toMsecs(data, 16);
// const origTimestampHigh = data.slice(24, 28).readUint32BE();
// const origTimestampLow = data.slice(28, 32).readUint32BE();
packet.originateTimestamp = toMsecs(data, 24);
// const rxTimestampHigh = data.slice(32, 36).readUint32BE();
// const rxTimestampLow = data.slice(36, 40).readUint32BE();
packet.rxTimestamp = toMsecs(data, 32);
// const txTimestampHigh = data.slice(40, 44).readUint32BE();
// const txTimestampLow = data.slice(44, 48).readUint32BE();
packet.txTimestamp = toMsecs(data, 40);
return packet;
}
bufferize(packet) {
const buffer = Buffer.alloc(48).fill(0x00);
buffer = (packet.leap << 6) | (packet.version << 3) | (this.mode << 0);
buffer = packet.stratum;
buffer = packet.poll;
buffer = packet.precision;
buffer.writeUInt32BE(packet.rootDelay, 4);
buffer.writeUInt32BE(packet.rootDispersion, 8);
buffer.writeUInt32BE(packet.referenceId, 12);
// Reference Timestamp
writeInMillis(buffer, 16, packet.referenceTimestamp, true);
// Originate timestamp
writeInMillis(
buffer,
24,
packet.originateTimestamp,
this.mode !== MODES.SERVER // Don't add NTP_DELTA if the packet is server mode
);
// RX Timestamp
writeInMillis(buffer, 32, packet.rxTimestamp, true);
// TX Timestamp
writeInMillis(buffer, 40, packet.txTimestamp, true);
return buffer;
}
}
function createPacket() {
const packet = new NTPPacket(MODES.CLIENT);
packet.originateTimestamp = Math.floor(Date.now() / 1000);
return packet.bufferize(packet);
}
function parse(buffer) {
const message = NTPPacket.parse(buffer);
message.destinationTimestamp = Math.floor(Date.now() / 1000) + NTP_DELTA;
message.time = new Date(Math.floor((message.rxTimestamp - NTP_DELTA) * 1000));
// Timestamp Name ID When Generated
// ------------------------------------------------------------
// Originate Timestamp T1 time request sent by client
// Receive Timestamp T2 time request received by server
// Transmit Timestamp T3 time reply sent by server
// Destination Timestamp T4 time reply received by client
const T1 = message.originateTimestamp;
const T2 = message.rxTimestamp;
const T3 = message.txTimestamp;
const T4 = message.destinationTimestamp;
// The roundtrip delay d and system clock offset t are defined as:
// -
// d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2
message.d = T4 - T1 - (T3 - T2);
message.t = (T2 - T1 + (T3 - T4)) / 2;
return message;
}
class Client {
constructor(
server = 'pool.ntp.org',
port = 123,
options = { timeout: 3000 }
) {
this.server = server;
this.port = port;
this.socket = udp.createSocket('udp4');
this.options = options;
return this;
}
async syncTime() {
return new Promise((resolve, reject) => {
this.socket = udp.createSocket('udp4');
const {
server,
port,
options: { timeout }
} = this;
const packet = createPacket();
this.socket.send(packet, 0, packet.length, port, server, err => {
if (err) {
this.socket.close();
return reject(err);
}
const timer = setTimeout(() => {
const error = new Error(
"NTP request timed out, server didn't answered"
);
this.socket.close();
return reject(error);
}, timeout);
this.socket.once('message', data => {
clearTimeout(timer);
const message = parse(data);
this.socket.close();
return resolve(message);
});
});
});
}
}
window.NtpClient = Client;
})();
页:
[1]