Open akerdi opened 1 year ago
// server/index.js
const udp = require('dgram');
const util = require('util')
const LogLevel = {
// ... more level
Info: 4,
Error: 3
}
const version = 1;
var logicInstance = null
function LogLog(level, msg) {
switch (level) {
case LogLevel.Info: {
console.log(`serv::${msg}`)
}
break;
case LogLevel.Error: {
console.error(`serv::${msg}`)
}
break;
default: {
throw new Error("Unbound Log Level! Got level: " + level)
}
}
}
function LogInfo(msg) {
LogLog(LogLevel.Info, msg)
}
function LogErr(msg) {
LogLog(LogLevel.Error, msg)
}
class CliInfo {
host = null
port = -1
constructor(host="localhost", port=65533) {
this.host = host
this.port = port
}
}
class UDPManager {
server = null // socket df
sendMessage(msg, info) {
if (info.port < 0) throw new Error(`port is invalid: Expect port > 0, Got port ${info.port}`)
const that = this
return new Promise((resolve, reject) => {
that.server.send(msg, info.port, info.host, function(error) {
if (error) {
reject(err)
} else {
resolve(true)
}
})
})
}
connectServ(cb) {
const that = this
this.server.on('error',function(error){
LogErr('Error: ' + error);
that.server.close();
});
// emits on new datagram msg
this.server.on('message',function(msg,info) {
const str = util.format('Received %d bytes from %s:%d\n',msg.length, info.address, info.port)
LogInfo(str);
cb && cb(msg, info)
});
this.server.on('listening', function() {
var address = that.server.address()
var port = address.port
var family = address.family
var ipaddr = address.address
LogInfo("Server is listening at port" + port)
LogInfo("Server ip :" + ipaddr)
LogInfo("Server is IP4/IP6 : " + family)
});
this.server.on('close',function(){
LogInfo("Socket is closed !")
})
}
createServ() {
this.server = udp.createSocket('udp4')
}
bindServ(port) {
if (!port) throw new Error("Param port is required")
this.server.bind(port)
}
closeServ() {
this.server.close()
}
static instance
static get sharedInstance() {
if (!UDPManager.instance) {
UDPManager.instance = new UDPManager
}
return UDPManager.instance
}
}
class Logic {
// demostrate getTimeFromNTPServer
routeTrigger(msg, cli) {
LogInfo("routeTrigger::start...")
const demoHost = cli.address
const demoport = cli.port
const cli_info = new CliInfo(demoHost, demoport)
this.startNTPServer(cli_info)
}
// info -> CliInfo
startNTPServer(info) {
LogInfo("startNTPServer::start...")
const date_millsec = Date.now()
LogInfo("--------", date_millsec)
const msgDataView = this.responseStruct(date_millsec)
UDPManager.sharedInstance.sendMessage(msgDataView, info)
.then(() => {
LogInfo("Msg send")
})
.catch(e => {
LogErr("startNTPServer::" + e.message || e)
})
}
stopNTPServe() {
UDPManager.sharedInstance.closeServ()
// clear UDPManager memory
}
responseStruct(dateStr) {
LogInfo("responseStruct::start...")
const dateStr_split_two_part_array = this.dateStrSplitApart(String(dateStr))
LogInfo("responseStruct::dateStr_split_two_part_array: " + dateStr_split_two_part_array)
const bufferArr = new ArrayBuffer(1+4+4);
const view = new DataView(bufferArr)
view.setUint8(0, version, false)
view.setUint32(1, dateStr_split_two_part_array[0], false)
view.setUint32(5, dateStr_split_two_part_array[1], false)
return Buffer.from(view.buffer)
}
dateStrSplitApart(dateStr) {
const first_part_length = Math.floor((dateStr.length+1) / 2)
const firstComponent = dateStr.substr(0, first_part_length)
const secondComponent = dateStr.substr(first_part_length, dateStr.length-first_part_length)
return [firstComponent, secondComponent]
}
testBuffer(buffer) {
LogInfo("------------------" + buffer.length)
const arrayBuffer = new Uint8Array(buffer.length)
for (let i = 0; i < buffer.length; i++) {
arrayBuffer[i] = buffer[i];
}
const view = new DataView(arrayBuffer.buffer, 0)
const version_number = view.getUint8(0, false)
const str1 = view.getUint32(1, false)
const str2 = view.getUint32(5, false)
LogInfo("testBuffer:: version: " + version_number + " str1: " + str1 + "||||||" + str2)
}
}
function main() {
logicInstance = new Logic
const port = 2222
UDPManager.sharedInstance.createServ();
UDPManager.sharedInstance.connectServ((msg, addr_info) => {
logicInstance.routeTrigger(msg, addr_info)
});
UDPManager.sharedInstance.bindServ(port);
}
main()
// client/index.js
var udp = require('dgram')
var client = udp.createSocket('udp4')
function toArrayBuffer(buffer) {
const arrayBuffer = new Uint8Array(buffer.length)
for (let i = 0; i < buffer.length; ++i) {
arrayBuffer[i] = buffer[i]
}
return arrayBuffer
}
client.on('message', function(netBuffer, info) {
// littleendian
const arrayBuffer = toArrayBuffer(netBuffer)
const view = new DataView(arrayBuffer.buffer, 0)
const version_number = view.getUint8(0, false)
const str1 = view.getUint32(1, false)
const str2 = view.getUint32(5, false)
console.log("-----:: version: " + version_number + " str1: " + str1+str2)
})
var data = Buffer.from('');
//sending multiple msg
client.send([data], 2222, 'localhost', function(error) {
if (error) {
client.close()
} else {
console.log('Data sent !!!')
}
});
实现一个伪NTP服务器类,提供startNTPServer方法,stopNTPServer方法,getTimeFromNTPServer方法。其中startNTPServer使用UDP4 socket,对外提供时间信息,信息需要以二进制字节的方式发送,第1字节写入uint8型数字作为版本号,第2字节到第9字节(共8个字节)写入当前时间戳毫秒数。stopNTPServer停止UDP4 socket的服务。getTimeFromNTPServer连接startNTPServer启动的UDP4 socket,获取版本信息和时间戳信息,并打印出来。