node.js - library - nodejs/express-transmite stdout instantáneamente al cliente
nodejs http server (3)
var spw = spawn(''ping'', [''-n'',''10'', ''127.0.0.1''])
el siguiente elemento secundario: var spw = spawn(''ping'', [''-n'',''10'', ''127.0.0.1''])
y me gustaría recibir los resultados de ping en el lado del cliente ( navegador ) uno por Uno , no en su conjunto.
Hasta ahora he intentado esto:
app.get(''/path'', function(req, res) {
...
spw.stdout.on(''data'', function (data) {
var str = data.toString();
res.write(str + "/n");
});
...
}
y eso:
...
spw.stdout.pipe(res);
...
En ambos casos, el navegador espera 10 de los pings para completar, y luego imprime el resultado como un todo. Me gustaría tenerlos uno por uno, ¿cómo lograrlo?
(El cliente está haciendo una llamada a .../path
y console.logs el resultado)
EDITAR: Aunque creo que los websockets son necesarios para implementar esto, solo quiero saber si hay otras formas. Vi varias respuestas de SO confusas y publicaciones de blog (en this publicación, en el paso uno, OP transmite los registros al navegador), lo cual no ayudó, por lo tanto, decidí buscar una recompensa por algo de atención.
Aquí hay un ejemplo completo usando SSE (eventos enviados por el servidor). Esto funciona en Firefox y probablemente en Chrome también:
var cp = require("child_process"),
express = require("express"),
app = express();
app.configure(function(){
app.use(express.static(__dirname));
});
app.get(''/msg'', function(req, res){
res.writeHead(200, { "Content-Type": "text/event-stream",
"Cache-control": "no-cache" });
var spw = cp.spawn(''ping'', [''-c'', ''100'', ''127.0.0.1'']),
str = "";
spw.stdout.on(''data'', function (data) {
str += data.toString();
// just so we can see the server is doing something
console.log("data");
// Flush out line by line.
var lines = str.split("/n");
for(var i in lines) {
if(i == lines.length - 1) {
str = lines[i];
} else{
// Note: The double-newline is *required*
res.write(''data: '' + lines[i] + "/n/n");
}
}
});
spw.on(''close'', function (code) {
res.end(str);
});
spw.stderr.on(''data'', function (data) {
res.end(''stderr: '' + data);
});
});
app.listen(4000);
Y el cliente HTML:
<!DOCTYPE Html>
<html>
<body>
<ul id="eventlist"> </ul>
<script>
var eventList = document.getElementById("eventlist");
var evtSource = new EventSource("http://localhost:4000/msg");
var newElement = document.createElement("li");
newElement.innerHTML = "Messages:";
eventList.appendChild(newElement);
evtSource.onmessage = function(e) {
console.log("received event");
console.log(e);
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
eventList.appendChild(newElement);
};
evtSource.onerror = function(e) {
console.log("EventSource failed.");
};
console.log(evtSource);
</script>
</body>
</html>
Ejecute node index.js
y apunte su navegador a http://localhost:4000/client.html
. Tenga en cuenta que tuve que usar la opción "-c" en lugar de "-n" ya que ejecuto OS X.
Esto no se puede lograr con el ciclo estándar de solicitud / respuesta HTTP. Básicamente, lo que está tratando de hacer es hacer un servidor "push" o "en tiempo real". Esto solo se puede lograr con xhr-polling o websockets.
Código de ejemplo 1:
app.get(''/path'', function(req, res) {
...
spw.stdout.on(''data'', function (data) {
var str = data.toString();
res.write(str + "/n");
});
...
}
Este código nunca envía una señal de fin y, por lo tanto, nunca responderá. Si agregara una llamada a res.end()
dentro de ese controlador de eventos, solo obtendrá el primer ping, que es el comportamiento esperado porque está terminando la secuencia de respuesta después de la primera parte de los datos de la salida estándar.
Ejemplo de código 2:
spw.stdout.pipe(res);
Aquí stdout está vaciando los paquetes al navegador, pero el navegador no procesará los fragmentos de datos hasta que se reciban todos los paquetes. Por lo tanto, la razón por la que espera 10 segundos y luego procesa la totalidad de la salida estándar. El mayor beneficio de este método es que no almacena en búfer la respuesta en la memoria antes de enviarla, lo que le permite mantener un peso liviano en su memoria.
Si está utilizando Google Chrome, cambiar el tipo de contenido a "text / event-stream" hace lo que está buscando.
res.writeHead(200, { "Content-Type": "text/event-stream" });
Vea mi esencia para ver un ejemplo completo: https://gist.github.com/sfarthin/9139500