readFile不在while循环内运行

时间:2018-09-19 21:25:42

标签: javascript node.js sockets express

每次尝试使用Express更新文本文件时,我都试图通过套接字连接发送文本文件的内容:

console.log('Server running!');

var express = require('express');
var app = express();
var server = app.listen(3000);
var fs = require("fs");
var x = 0;

app.use(express.static('public'));

var socket = require('socket.io');
var io = socket(server);

io.sockets.on('connection', newConnection);

function newConnection(socket) {
  console.log("New connection: " + socket.id);
  while (true) {
    fs.readFile('song.txt', function(err, data) {
      if (err) throw err;
      console.log(data);
      if (data != x) {
        var songdata = data;
        console.log(songdata);
        io.sockets.emit('update', songdata);
        x = data;
      } else {
        console.log("Song is not different:)");
      }
    })
  }
}

没有while循环,一切正常,我在单独的客户端中收到了内容。但是,现在什么也没有发生,没有控制台日志数据。这表明readFile突然不再运行,为什么?

谢谢:)

2 个答案:

答案 0 :(得分:1)

首先,一些基本知识。 node.js将Javascript作为单线程运行,因此这是一台单线程服务器。一次只能用Java语言做一件事。但是,如果仔细编程,它可以很好地扩展并做很多事情。

第二,您几乎永远不想在服务器端Javascript中进行while (true)。这将永远运行,永远不要让其他任何东西在您的服务器上运行。没什么。

第三,您尝试在每次新客户端连接时创建该无限循环的新版本。这不是一个正确的设计(即使没有无限循环)。您只需要一个代码实例来检查文件,而无需N。

现在,如果您真正想做的是“轮询” song.txt中的更改并在更改时通知客户端,则需要在检查文件和更改之间选择一个合理的时间增量。使用计时器。这样会经常检查该文件,并让您的服务器在其余时间正常运行。

这是一个简单的版本,以setInterval()进行轮询:

console.log('Server code started!');

const express = require('express');
const app = express();
const server = app.listen(3000);
const fs = require("fs");
let lastSongData = 0;

app.use(express.static('public'));

const io = require('socket.io')(server);

// get initial songData for future use
// there will not be any connected clients yet so we don't need to broadcast it
try {
    lastSongData = fs.readFileSync('song.txt');
} catch(e) {
    console.log(e, "\nDidn't find song.txt on server initialization");
}

// here, we create a polling loop that notifies all connected clients 
// any time the song has changed
const pollInterval = 60*1000;   // 60 seconds, ideally it should be longer than this
const pollTimer = setInterval(() => {
    fs.readFile('song.txt', (err, songData) => {
        if (!err && songData !== lastSongData) {
            // notify all connect clients
            console.log("found changed songData");
            io.emit('update', songData);
            lastSongData = songData;
        }
    });
}, pollInterval);

io.sockets.on('connection', socket => {
  console.log("New connection: " + socket.id);
});

如果您的songData是二进制的,则必须更改将数据发送到客户端的方式以及将数据与以前的数据进行比较的方式,以便发送和接收二进制数据,而不是字符串数据和所以您在比较缓冲区,而不是字符串。

以下是有关使用socket.io发送二进制数据的一些参考:

How to send binary data with socket.io?

How to send binary data from a Node.js socket.io server to a browser client?


检测文件更改的一种更有效的方法是使用fs.watch(),它应将文件更改通知您,尽管您将必须在运行的任何平台上进行全面测试以确保其正常运行您想要的方式。该功能有多个platform caveats(并非在所有平台上都可以正常使用),因此您必须在平台上进行全面测试,以查看是否可以将其用于所需的功能。

console.log('Server code started!');

const express = require('express');
const app = express();
const server = app.listen(3000);
const fs = require("fs");
let lastSongData = 0;

app.use(express.static('public'));

const io = require('socket.io')(server);

// get initial songData for future use
// there will not be any connected clients yet so we don't need to broadcast it
try {
    lastSongData = fs.readFileSync('song.txt');
} catch(e) {
    console.log(e, "\nDidn't find song.txt on server initialization");
}

// ask node.js to tell us when song.txt is modified
fs.watch('song.txt', (eventType, filename) => {
    // check the file for all eventTypes
    fs.readFile('song.txt', (err, songData) => {
        if (!err && songData !== lastSongData) {
            // notify all connect clients
            console.log("found changed songData");
            lastSongData = songData;
            io.emit('update', songData);
        }
    });
});

io.sockets.on('connection', socket => {
  console.log("New connection: " + socket.id);
});

根据原始代码尚不清楚,是否需要将songData发送到每个新连接(无论最近是否更改)。

如果是这样,您只需将连接事件处理程序更改为此:

io.sockets.on('connection', socket => {
  console.log("New connection: " + socket.id);
  // send most recent songData to each newly connected client
  if (lastSongData) {
      socket.emit('update', lastSongData);
  }
});

答案 1 :(得分:0)

连续读取文件以检测更改不是一个好主意。相反,您应该使用fs.watch(filename[, options][, listener])来在文件更改时通知您。当新的套接字仅连接该套接字时,应该向其广播内容,将其发送给每个客户端是多余的。

io.sockets.on('connection', newConnection);

var filename = 'song.txt';

function update(socket) {
    fs.readFile(filename, function (err, data) {
        if (err) throw err;
        socket.emit('update', data);
    });
}

function newConnection(socket) {
    console.log("New connection: " + socket.id);
    update(socket); // Read and send to just this socket
}

fs.watch(filename, function () {
    console.log("File changed");
    update(io.sockets); // Read and send to all sockets.
});