烁灵 发表于 2024-6-12 16:52:59

【含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]
查看完整版本: 【含MV】获取授时中心时间