| 
 | 
 
研究了一下 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[offset + i];
 
 -                 }
 
  
-                 for (let i = 4; i < 8; ++i) {
 
 -                         fraction = fraction * 256 + buffer[offset + i];
 
 -                 }
 
  
-                 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[offset + 0] = (seconds & 0xff000000) >> 24;
 
 -                 buffer[offset + 1] = (seconds & 0x00ff0000) >> 16;
 
 -                 buffer[offset + 2] = (seconds & 0x0000ff00) >> 8;
 
 -                 buffer[offset + 3] = seconds & 0x000000ff;
 
  
-                 // fraction
 
 -                 buffer[offset + 4] = (fraction & 0xff000000) >> 24;
 
 -                 buffer[offset + 5] = (fraction & 0x00ff0000) >> 16;
 
 -                 buffer[offset + 6] = (fraction & 0x0000ff00) >> 8;
 
 -                 buffer[offset + 7] = 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[0] >> 6) & 0x3;
 
 -                         packet.version = (data[0] >> 3) & 0x7;
 
 -                         packet.mode = data[0] & 0x7;
 
 -                         packet.stratum = parseInt(data[1]) || 2;
 
 -                         packet.poll = parseInt(data[2]) || 10;
 
 -                         packet.precision = parseInt(data[3]);
 
 -                         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[0] = (packet.leap << 6) | (packet.version << 3) | (this.mode << 0);
 
 -                         buffer[1] = packet.stratum;
 
 -                         buffer[2] = packet.poll;
 
 -                         buffer[3] = 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;
 
 - })();
 
  复制代码 
 
 |   
 
 
 
 |