Los streams son colecciones/flujos de datos — algo parecido a los Arrays
o los Strings
.
La diferencia es que los datos del stream pueden no estar siempre disponibles, y pueden "contener" más datos que el límite de la memoria.
Una de las propiedades más importantes es el encadenamiento, podemos modificar los datos iniciales como si fuésemos encadenando comando de bash:
$ echo "Hola" | grep "o"
Ejemplos de streams en Node.js:
Readable
(lectura): Es una abstracción de un conjunto de datos de entrada, por ejemplofs.createReadStream()
.Writable
(escritura): Es una abstracción del destino en el que será escrito, por ejemplofs.createWriteStream()
.Duplex
(lectura y escritura): Por ejemplo un socket TCP.Transform
: Un stream que a parte de leer y escribir va transformando los datos a medida que van llegando, por ejemplozlib.createGzip()
.
Todos los streams son instancias de
EventEmitter
, emiten eventos a medida que leen y escriben datos. Sin embargo, podemos consumir y encadenar streams de una manera sencilla utilizando la funciónpipe()
.
readableSrc
.pipe(transformStream1)
.pipe(transformStream2)
.pipe(finalWrtitableDest)
La función pipe()
devuelve la salida del stream anterior:
a.pipe(b).pipe(c).pipe(d)
// Es equivalente a:
a.pipe(b);
b.pipe(c);
c.pipe(d);
// En Linux, es equivalente a:
$ a | b | c | d
Los eventos más importantes de un stream de lectura son:
data
: Cada vez que se procesa un trozo del dato.end
: Cuando ya se han emitido la totalidad de los datos.
Los eventos más importantes de un stream de escritura son:
drain
: Cuando el stream está disponible para recibir más datos.finish
: Cuando ya se han liberado todos los datos del stream (se vacía).
// readable.pipe(writable)
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => {
writable.end();
});
const { Readable, Writable } = require('stream');
const inStream = new Readable({
read(size) {
this.push(String.fromCharCode(this.currentCharCode++));
if (this.currentCharCode > 90) {
this.push(null);
}
}
});
const outStream = new Writable({
write(chunk, encoding, callback) {
console.log(chunk.toString())
callback();
}
});
inStream.currentCharCode = 65;
inStream.pipe(outStream);
const { Transform } = require('stream');
const upperCaseTr = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
process.stdin.pipe(upperCaseTr).pipe(process.stdout);
WebSocket es una tecnología que proporciona un canal de comunicación bidireccional y full-duplex sobre un único socket TCP. Está diseñada para ser implementada en navegadores y servidores web, pero puede utilizarse por cualquier aplicación cliente/servidor.
- Protocolo de comunicación que se negocia sobre HTTP
- Full-duplex
- Única conexión permanente (siempre conectado)
- Stream de mensajes
- Contenido en tiempo real
- Orientado a "eventos" (mensajes)
- Baja latencia
Entendiendo los eventos
Negociación del protocolo WebSocket
Para establecer una conexión WebSocket, el cliente manda una petición de negociación WebSocket, y el servidor manda una respuesta de negociación WebSocket, como se puede ver en el siguiente ejemplo: WebSockets en Wikiwand
-
Cliente:
GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Sec-WebSocket-Protocol: sample Upgrade: WebSocket Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 Origin: http://example.com ^n:ds[4U
-
Servidor:
HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Origin: http://example.com Sec-WebSocket-Location: ws://example.com/demo Sec-WebSocket-Protocol: sample 8jKS'y:G*Co,Wxa-
Los 8 bytes con valores numéricos que acompañan a los campos Sec-WebSocket-Key1 y Sec-WebSocket-Key2 son tokens aleatorios que el servidor utilizará para construir un token de 16 bytes al final de la negociación para confirmar que ha leído correctamente la petición de negociación del cliente.
Abrir la conexión
const myWebSocket = new WebSocket('ws://www.websockets.org');
Gestión de Eventos
Siempre dispondremos del parametro event.
myWebSocket.onopen = function() { console.log("Connection open ..."); };
myWebSocket.onmessage = function(evt) { console.log( "Received Message: ", evt.data); };
myWebSocket.onclose = function() { console.log("Connection closed."); };
Envio de mensajes
myWebSocket.send('Hello WebSockets!');
Cerrar la conexión
myWebSocket.close();
Características:
- Fácil
- Soporte a navegadores obsoletos (Fallback)
- Popular
- Extraordinariamente simple
Abrir la conexión:
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
</script>
Gestión de Eventos:
socket.on('my-event', (data) => {
console.log('Received Message:', data);
});
Eventos reservados:
connect
socket.on('connect', (socket) => {
// ...
});
disconnect
socket.on('disconnect', (socket) => {
// ...
});
error
socket.on('error', (error) => {
// ...
});
Envío de mensajes:
socket.emit('my-event', data);
Cerrar la conexión:
socket.disconnect();
socket.close();
// Si quieres reabrir. -> socket.connect();
Con HTTP directamente:
const server = require('http').createServer();
const io = require('socket.io')(server);
io.on('connection', (socket) =>{
socket.on('event', (data) =>{
// ...
});
socket.on('disconnect', (socket) =>{
// ...
});
});
server.listen(8080);
Con Express:
const app = require('express')();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
io.on('connection', () => {
// ...
});
server.listen(8080);
Eventos reservados:
connect
io.on('connect', (socket) => {
// ...
});
disconnect
io.on('connection', (socket) => {
socket.on('disconnect', (reason) => {
// ...
});
});
error
io.on('connection', (socket) => {
socket.on('error', (error) => {
// ...
});
});
Cheatsheet emit
:
// sending to sender-client only
socket.emit('message', "this is a test");
// sending to all clients, include sender
io.emit('message', "this is a test");
// sending to all clients except sender
socket.broadcast.emit('message', "this is a test");
// sending to all clients in 'game' room(channel) except sender
socket.broadcast.to('game').emit('message', 'nice game');
// sending to all clients in 'game' room(channel), include sender
io.in('game').emit('message', 'cool game');
// sending to sender client, only if they are in 'game' room(channel)
socket.to('game').emit('message', 'enjoy the game');
// sending to all clients in namespace 'myNamespace', include sender
io.of('myNamespace').emit('message', 'gg');
// sending to individual socketid
socket.broadcast.to(socketid).emit('message', 'for your eyes only');
Compartiendo sesión entre express
y socket.io
:
const sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: 'keyboard cat',
});
// express
app.use(sessionMiddleware);
// socket.io
io.use((socket, next) => {
sessionMiddleware(socket.request, socket.request.res, next);
});
Recursos:
- Differences between socket.io and websockets en Stackoverflow
- WebSocket and Socket.IO by DWD
- An Introduction to WebSockets by Matt West
Documentación:
Instalación del Toolbelt:
- Universal, mediante
npm
# npm
npm install -g heroku
#Linux
wget -O- https://toolbelt.heroku.com/install-ubuntu.sh | sh
# macOS (brew)
brew install heroku/brew/heroku
Login en Heroku:
heroku login
Preparando la Aplicación:
git clone https://github.com/heroku/node-js-getting-started.git && cd node-js-getting-started
Crear un proyecto:
# With a random name
heroku create
# With custom name
heroku create miproyecto
Desplegando nuestro proyecto:
Previamente tienes que tener el proyecto gestionado con Git.
git push heroku master
Escalando nuestro proyecto:
# Retrieve the number of "Dynos"
heroku ps
# Scale the web app
heroku ps:scale web=1
Logs del proyecto:
heroku logs --tail
Ejecutar el proyecto en local:
heroku local web
- Documentación General
- Lista de Addons
- Raygun
- Informes de error en tiempo real
- Documentación para Node
- SendGrid
- Sistema de envio de emails con muchos extras
- Documentación para Node
- Easy SMS
- Envio/Recepción de SMS mundial
- Documentación
- mLab MongoDB
- MongoDB as a Service
- Documentación para Node
- GrapheneDB
- Neo4j Graph as a Service
- Documentación para Node
- ClearDB MySQL
- MySQL
- Documentación
- Graph Story
- Enterprise Neo4j Graph Databases as a Service
- Documentación
- Heroku Scheduler
- Programador de tareas
- Documentación
- Process Scheduler
- Programa el escalamiento de tus process types y dynos por horas
- Documentación
- DocRaptor
- Conversor de HTML a PDF. Simple y robusto
- Documentación
- Tinfoil Security
- Escaner de seguridad
- Documentación
- Auth0
- Gestión de credenciales
- Documentación del Addon
- Documentación para Node
- OAuth.io
- Gestión de credenciales. Más de 100 provedores
- Documentación
- CloudAMQP
- RabbitMQ as a Service
- Documentación para Node
- Keen IO
- Analíticas para desarrolladores
- Documentación para Node
- Bonsai Elasticsearch
- Elasticsearch
- Documentación
- Raygun
ndb:
- Instalación global.
- Utilizar
debugger;
para lanzar las herrameintas de depuración del navegador. - Depuración mediante Chromium.
ndb server.js
nodemon: Relanza la aplicación por cada cambio que realicemos en diferentes ficheros.
nodemon server.js
Relanza la aplicación cuando deja de funcionar
forever start/stop server.js
pm2: Monitorización activa de la aplicación
pm2 start/stop server.js
1 - Partiendo del repositorio simple-chat#template implementa toda la parte de servidor utilizando la librería socket.io
.