readFile не работает внутри цикла - PullRequest
0 голосов
/ 20 сентября 2018

Я пытаюсь отправить содержимое текстового файла через сокет-соединение каждый раз, когда текстовый файл обновляется с помощью 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 голосов
/ 20 сентября 2018

Постоянное чтение файла для обнаружения изменений не очень хорошая идея.Вместо этого вы должны использовать 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.
});
0 голосов
/ 20 сентября 2018

Прежде всего, некоторые основы.node.js запускает ваш Javascript как однопоточный, и, таким образом, это однопоточный сервер.Он может делать только одну вещь с вашим Javascript одновременно.Но если вы тщательно его запрограммируете, он может очень хорошо масштабироваться и делать много вещей.

Во-вторых, вы почти никогда не захотите делать while (true) в Javascript на стороне сервера.Это будет работать вечно и никогда не позволять ничему другому работать на вашем сервере.Ничего другого.

В-третьих, вы пытаетесь создать новую версию этого бесконечного цикла каждый раз, когда подключается новый клиент.Это неправильный дизайн (даже если не было бесконечного цикла).Вам нужен только один экземпляр кода, проверяющий файл, а не 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:

Как отправлять двоичные данные с помощью socket.io?

Как отправить двоичные данные с сервера Node.js socket.io в клиент браузера?


Немного более эффективный способ обнаружения изменений в файле - это использовать fs.watch(), которыйдолжен уведомить вас об изменениях в файле, хотя вам придется тщательно протестировать его на любой платформе, на которой вы работаете, чтобы убедиться, что он работает так, как вы хотите.Эта функция имеет ряд предостережений платформы (она не работает одинаково на всех платформах), поэтому вы должны тщательно протестировать ее на своей платформе, чтобы увидеть, можете ли вы использовать ее для того, что вы хотите.

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);
  }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...