let http = require("http")
let url = require("url")
let fs = require("fs")
let net = require('net')
let crypto = require('crypto')
let isModify = false // 文件是否修改
let websocketInstant // 存放每一个客户端socket的数组
let port = 8100
// 默认端口,并检测端口是否被占用
portIsOccupied(port).then(() => {
createServerFn();
})
function createServerFn() {
let app = http.createServer(function (req, res) {
let reqPath = url.parse(req.url).path
let filepath = __dirname + reqPath
fs.exists(filepath, function (exists) {
if (exists) {
fs.stat(filepath, function (err, stats) {
if (err) {
res.end('<h1>server error</h1><p>' + err + '</p>')
} else {
if (stats.isFile()) {
console.log('Date:'+ dateFormat() + ' '+filepath.replace(/\\/g, '/'))
if (filepath.includes('html')) {
dealHtmlFile(filepath, res)
return
} else {
let file = fs.createReadStream(filepath)
file.pipe(res)
}
} else {
fs.readdir(filepath, function (err, files) {
if (files.includes('index.html')) {
filepath = filepath + 'index.html'
console.log('Date:'+ dateFormat() + ' '+filepath.replace(/\\/g, '/'))
dealHtmlFile(filepath, res)
} else {
res.end('<h1>404 no such file or directory</h1><p>no such file or directory</p><p>' + filepath + '/index.html</p>')
}
})
}
}
})
} else {
res.end('<h1>404 no such file or directory</h1><p>no such file or directory</p><p>' + filepath + '</p>')
}
})
})
createSocket(app)
app.listen(port)
console.log('http.createServer is start, port ' + port)
}
// 检测端口是否被占用
function portIsOccupied() {
const server = net.createServer().listen(port)
return new Promise((resolve, reject) => {
server.on('listening', () => {
server.close()
resolve()
})
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
port++
resolve(portIsOccupied())
} else {
reject(err)
}
})
})
}
// 检测文件是否修改
fs.watch(__dirname,{recursive: true}, function (event, filename) {
isModify = true
})
// html 文件特殊处理
function dealHtmlFile(filepath, res) {
let data = fs.readFileSync(filepath, 'utf-8')
let scriptStr = `
<script>
let ws = new WebSocket("ws:"+location.host);
ws.onopen = function(evt) {
console.log('Connection open ...')
setInterval(function(){
ws.send('check file modify...')
},1000)
};
ws.onmessage = function(evt) {
let data = JSON.parse(evt.data);
if (data.refresh && data.refresh == true) {
location.reload()
}
}
ws.onclose = function(evt) {
console.log('Connection closed.')
}
</script>
`
data += scriptStr
res.end(data)
}
// 添加wesocket
// https://github.com/yangmei123/node-websocket/blob/master/nodejs-socket.js
function createSocket(server) {
server.on('upgrade', (req, socket, head) => { // 在收到upgrade请求后,告知客户端允许切换协议
const val = crypto.createHash('sha1')
.update(req.headers['sec-websocket-key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary')
.digest('base64');
const frame = {
buffer: new Buffer(0),
}
socket.setNoDelay(true)
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'Sec-WebSocket-Accept:' + val + '\r\n' +
'\r\n');
socket.on('data', sendRecive.bind(null, socket, frame));
websocketInstant = socket
// 解码
function decodeDataFrame(e) {
let i = 0, j, arrs = [];
let frame = {
FIN: e[i] >> 7, // 右移7位等于1 e[0]转为二进制再右移
Opcode: e[i++] & 15, //Opcode占第一个字节二进制后4位,和1111做与比较
Mask: e[i] >> 7, // e[1]二进制第一位
PayloadLength: e[i++] & 0x7F // e[1]二进制的后7位和(1111111) 做与运算
};
if (frame.PayloadLength === 126) {// 处理特殊长度126和127
frame.PayloadLength = (e[i++] << 8) + e[i++]
}
if (frame.PayloadLength === 127) {
i += 4; // 长度一般用4个字节的整型,前四个字节一般为长整型留空的。
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++]
}
if (frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]]
for (j = 0, arrs = []; j < frame.PayloadLength; j++) {
arrs.push(e[i + j] ^ frame.MaskingKey[j % 4])
}
} else {
arrs = e.slice(i, i + frame.PayloadLength)
}
arrs = new Buffer(arrs)
if (frame.Opcode === 1) { // 是文本格式的
arrs = arrs.toString()
}
frame.PayloadData = arrs
return frame // 返回数据帧
}
function sendRecive(socket, frame, data) {
// const readableData = decodeDataFrame(data).PayloadData; // 接受浏览器的数据并返回文件是否修改
websocketInstant.write(encodeDataFrame({
FIN: 1,
Opcode: 1,
MASK: 0,
PayloadData: '{"refresh": ' + isModify + '}'
}))
isModify = false
}
});
}
// 编码算法
function encodeDataFrame(e) {
let bufferArr = [];
let PayloadData = Buffer.from(e.PayloadData) // 放到buffer
const PayloadLength = PayloadData.length
const fin = e.FIN << 7 // 转为2进制
bufferArr.push(fin + e.Opcode) // 第一个字节拼好
if (PayloadLength < 126) bufferArr.push(PayloadLength)
else if (PayloadLength < 0x10000) bufferArr.push(126, (PayloadLength & 0xFF00) >> 8, PayloadLength & 0xFF)
else bufferArr.push(
127, 0, 0, 0, 0, //8字节数据,前4字节一般没用留空
(PayloadLength & 0xFF000000) >> 24,
(PayloadLength & 0xFF0000) >> 16,
(PayloadLength & 0xFF00) >> 8,
PayloadLength & 0xFF
)
return Buffer.concat([new Buffer(bufferArr), PayloadData])
}
// 日期格式化
function dateFormat() {
let date = new Date();
let year = date.getFullYear()
let month = date.getMonth()
let day = date.getDate()
let hours = date.getHours()
let minutes = date.getMinutes()
let seconds = date.getSeconds()
return year+'/'+ month+'/'+day+' '+ hours+':'+minutes+':'+seconds
}
网友评论