FS writeFile(Sync) 导致 ws.broadcast 无法工作
FS writeFile(Sync) causing ws.broadcast to not work
我试图通过在 NodeJS 中编写一个简单的聊天应用程序来学习 WebSocket
API。我需要将消息保存在一个名为 messages.json
的文件中,该文件看起来像这样:
[
[
"username",
"message"
],
[
"username",
"message"
],
...
]
我已经完成了客户端部分。 HTML 和 Javascript 文件如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Stuff</title>
<link href="style.css" rel="stylesheet" />
</head>
<body>
<input id="chatText" placeholder="Message" />
<button
id="sendBtn"
onclick='if (document.getElementById("chatText").value.trim() !== "") ws.send(document.getElementById("chatText").value + " " + user); document.getElementById("chatText").value = ""'
>
<span style="margin-left: 5px"></span>Send<span
style="margin-left: 5px"
></span>
</button>
<br />
<script src="script.js"></script>
</body>
</html>
var user = prompt('Username?').trim()
while (user.split(' ').length > 1 || user.trim() === '') {
user = prompt('Username?').trim()
}
var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function (event) {
console.log('Connection successfully opened!');
};
ws.onerror = function (err) {
console.log('err: ', err);
}
ws.onmessage = function (event) {
var div = document.createElement('div')
div.classList.add('chatMsg')
div.innerText = event.data.split(/ (?=[^ ]+$)/)[1] + ': ' + event.data.split(/ (?=[^ ]+$)/)[0]
document.body.appendChild(div)
};
我使用正则表达式在最后一个 space 字符处拆分数据,因为我发现我无法将数组或对象从客户端发送到服务器,反之亦然,并且用户名不能包含任何 space,尽管我可能不小心把事情弄得太复杂了。
然后,服务器端的NodeJS代码就得写了。当我想永久保存消息时,我就完成了它:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {
res.sendFile('public/index.html');
});
app.listen(8000, () => console.log('server started'));
const WebSocketServer = require('ws').Server
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', ((ws) => {
ws.on('message', (message) => {
wss.broadcast(message);
});
ws.on('end', () => {
console.log('Connection ended...');
});
}));
wss.broadcast = function broadcast(msg) {
wss.clients.forEach(function each(client) {
client.send(msg);
});
};
到目前为止,我能够从一个浏览器选项卡发送消息,而另一个选项卡能够正确接收消息并发送另一条消息,依此类推。我认为永久保存消息的最佳方式是 JSON 文件,所以我创建了一个名为 messages.json
的文件,上面提到了
在程序开始时,我通过包含 var messages = require('./messages.json')
和 require
d 模块 fs
来读取文件以写入 messages.json
.
const fs = require('fs');
const messages = require('./messages.json');
然后我将 ws.on('message', (message) => { ...
更改为如下所示:
ws.on('message', (message) => {
messages.push(message.split(/ (?=[^ ]+$)/))
fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
wss.broadcast(message);
});
然后,我在 file:// ... /public/index.html
的浏览器中打开了两个选项卡。我从第一个选项卡发送了一条消息。然后我切换到第二个选项卡并尝试发送消息。我在输入框中输入了一些内容并按下了“发送”按钮,输入框内的文本就如预期的那样消失了,但除此之外什么也没有发生。然后我切换回第一个选项卡并尝试了同样的事情,但它的行为方式与第二个选项卡相同。我切换回 VSCode window 并检查终端,但没有错误。我打开 messages.json
并在数组中只看到一条消息。该文件如下所示:
[
[
"user",
"test"
]
]
我很困惑,因为我发送了三条消息,但文件中只记录了一条。我将 messages.json
重置为 []
并使用 fs.readFileSync
而不是 fs.readFile
。没有任何变化,然后,为了调试目的,我将行 fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
更改为 console.log('test')
。我关闭了两个选项卡并再次打开它们。然后,我从第一个选项卡发送了一条消息,从第二个选项卡发送了一条消息,从第一个选项卡发送了另一条消息。一切都按预期进行,两个选项卡都显示了消息。终端显示输出:
test
test
test
它按预期工作。为什么它不适用于 fs.readFile
和 fs.readFileSync
?
到 运行 并重新加载服务器,我正在使用名为 live
的 NPM 脚本和 nodemon
:
"scripts": {
"live": "nodemon index.js"
},
要启动服务器,我只需在终端中 运行 nodemon index.js
。
我的目录结构:
node_modules
public
|-- index.html
|-- script.js
index.js
messages.json
package.json
问题是我使用 require()
读取 JSON 文件,因为这是更短、更容易的方法。我能够通过使用 JSON.parse(fs.readFileSync('./messages.json', () => { }).toString())
而不是 require('./messages.json')
.
来修复它
const express = require('express')
const bodyParser = require('body-parser')
const fs = require('fs')
const app = express()
var messages = JSON.parse(fs.readFileSync('./messages.json', () => { }).toString())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static('public'))
app.get('/', (req, res) => {
res.sendFile('public/index.html')
})
app.listen(8000, () => { })
const WebSocketServer = require('ws').Server
const wss = new WebSocketServer({ port: 8080 })
wss.on('connection', ((ws) => {
ws.on('message', (message) => {
messages.push(message.split(/ (?=[^ ]+$)/))
fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
wss.broadcast(message)
})
messages.forEach((item) => {
ws.send(item.join(' '))
})
ws.on('end', () => {
console.log('Connection ended...')
})
}))
wss.broadcast = function broadcast(msg) {
wss.clients.forEach(function each(client) {
client.send(msg)
})
}
编辑:
正如@Bergi 在评论中告诉我的那样,这是因为 nodemon
- 它正在将文件视为依赖项(懒惰地使用 require()
读取 JSON 文件)并在每次编辑 messages.json
时重新启动服务器,这就是为什么它在第一条聊天消息后不起作用的原因。我能够通过 运行 我的应用程序使用 node
而不是 nodemon
来确认这一点。
我试图通过在 NodeJS 中编写一个简单的聊天应用程序来学习 WebSocket
API。我需要将消息保存在一个名为 messages.json
的文件中,该文件看起来像这样:
[
[
"username",
"message"
],
[
"username",
"message"
],
...
]
我已经完成了客户端部分。 HTML 和 Javascript 文件如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Stuff</title>
<link href="style.css" rel="stylesheet" />
</head>
<body>
<input id="chatText" placeholder="Message" />
<button
id="sendBtn"
onclick='if (document.getElementById("chatText").value.trim() !== "") ws.send(document.getElementById("chatText").value + " " + user); document.getElementById("chatText").value = ""'
>
<span style="margin-left: 5px"></span>Send<span
style="margin-left: 5px"
></span>
</button>
<br />
<script src="script.js"></script>
</body>
</html>
var user = prompt('Username?').trim()
while (user.split(' ').length > 1 || user.trim() === '') {
user = prompt('Username?').trim()
}
var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function (event) {
console.log('Connection successfully opened!');
};
ws.onerror = function (err) {
console.log('err: ', err);
}
ws.onmessage = function (event) {
var div = document.createElement('div')
div.classList.add('chatMsg')
div.innerText = event.data.split(/ (?=[^ ]+$)/)[1] + ': ' + event.data.split(/ (?=[^ ]+$)/)[0]
document.body.appendChild(div)
};
我使用正则表达式在最后一个 space 字符处拆分数据,因为我发现我无法将数组或对象从客户端发送到服务器,反之亦然,并且用户名不能包含任何 space,尽管我可能不小心把事情弄得太复杂了。
然后,服务器端的NodeJS代码就得写了。当我想永久保存消息时,我就完成了它:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {
res.sendFile('public/index.html');
});
app.listen(8000, () => console.log('server started'));
const WebSocketServer = require('ws').Server
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', ((ws) => {
ws.on('message', (message) => {
wss.broadcast(message);
});
ws.on('end', () => {
console.log('Connection ended...');
});
}));
wss.broadcast = function broadcast(msg) {
wss.clients.forEach(function each(client) {
client.send(msg);
});
};
到目前为止,我能够从一个浏览器选项卡发送消息,而另一个选项卡能够正确接收消息并发送另一条消息,依此类推。我认为永久保存消息的最佳方式是 JSON 文件,所以我创建了一个名为 messages.json
的文件,上面提到了
在程序开始时,我通过包含 var messages = require('./messages.json')
和 require
d 模块 fs
来读取文件以写入 messages.json
.
const fs = require('fs');
const messages = require('./messages.json');
然后我将 ws.on('message', (message) => { ...
更改为如下所示:
ws.on('message', (message) => {
messages.push(message.split(/ (?=[^ ]+$)/))
fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
wss.broadcast(message);
});
然后,我在 file:// ... /public/index.html
的浏览器中打开了两个选项卡。我从第一个选项卡发送了一条消息。然后我切换到第二个选项卡并尝试发送消息。我在输入框中输入了一些内容并按下了“发送”按钮,输入框内的文本就如预期的那样消失了,但除此之外什么也没有发生。然后我切换回第一个选项卡并尝试了同样的事情,但它的行为方式与第二个选项卡相同。我切换回 VSCode window 并检查终端,但没有错误。我打开 messages.json
并在数组中只看到一条消息。该文件如下所示:
[
[
"user",
"test"
]
]
我很困惑,因为我发送了三条消息,但文件中只记录了一条。我将 messages.json
重置为 []
并使用 fs.readFileSync
而不是 fs.readFile
。没有任何变化,然后,为了调试目的,我将行 fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
更改为 console.log('test')
。我关闭了两个选项卡并再次打开它们。然后,我从第一个选项卡发送了一条消息,从第二个选项卡发送了一条消息,从第一个选项卡发送了另一条消息。一切都按预期进行,两个选项卡都显示了消息。终端显示输出:
test
test
test
它按预期工作。为什么它不适用于 fs.readFile
和 fs.readFileSync
?
到 运行 并重新加载服务器,我正在使用名为 live
的 NPM 脚本和 nodemon
:
"scripts": {
"live": "nodemon index.js"
},
要启动服务器,我只需在终端中 运行 nodemon index.js
。
我的目录结构:
node_modules
public
|-- index.html
|-- script.js
index.js
messages.json
package.json
问题是我使用 require()
读取 JSON 文件,因为这是更短、更容易的方法。我能够通过使用 JSON.parse(fs.readFileSync('./messages.json', () => { }).toString())
而不是 require('./messages.json')
.
const express = require('express')
const bodyParser = require('body-parser')
const fs = require('fs')
const app = express()
var messages = JSON.parse(fs.readFileSync('./messages.json', () => { }).toString())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static('public'))
app.get('/', (req, res) => {
res.sendFile('public/index.html')
})
app.listen(8000, () => { })
const WebSocketServer = require('ws').Server
const wss = new WebSocketServer({ port: 8080 })
wss.on('connection', ((ws) => {
ws.on('message', (message) => {
messages.push(message.split(/ (?=[^ ]+$)/))
fs.writeFile('./messages.json', JSON.stringify(messages), () => { })
wss.broadcast(message)
})
messages.forEach((item) => {
ws.send(item.join(' '))
})
ws.on('end', () => {
console.log('Connection ended...')
})
}))
wss.broadcast = function broadcast(msg) {
wss.clients.forEach(function each(client) {
client.send(msg)
})
}
编辑:
正如@Bergi 在评论中告诉我的那样,这是因为 nodemon
- 它正在将文件视为依赖项(懒惰地使用 require()
读取 JSON 文件)并在每次编辑 messages.json
时重新启动服务器,这就是为什么它在第一条聊天消息后不起作用的原因。我能够通过 运行 我的应用程序使用 node
而不是 nodemon
来确认这一点。