Skip to content

Latest commit

 

History

History
executable file
·
580 lines (422 loc) · 14.6 KB

README.md

File metadata and controls

executable file
·
580 lines (422 loc) · 14.6 KB

Clase 3

Streams

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:

streams

Tipos de streams

  • Readable (lectura): Es una abstracción de un conjunto de datos de entrada, por ejemplo fs.createReadStream().
  • Writable (escritura): Es una abstracción del destino en el que será escrito, por ejemplo fs.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 ejemplo zlib.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ón pipe().

La función pipe()

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

Eventos

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();
});

stream-events

Implementando un stream de lectura y uno de escritura

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);

Implementando un stream de transformación

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);

Más info

WebSockets

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.

WS_Sockets

  • 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

Sockets

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-
    

handshake

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.

WS: Nativo

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();

Socket.io

Sockets

Características:

  • Fácil
  • Soporte a navegadores obsoletos (Fallback)
  • Popular
  • Extraordinariamente simple

Client side

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) => {
  // ...
});

Más en la documentación

Envío de mensajes:

socket.emit('my-event', data);

Cerrar la conexión:

socket.disconnect();
socket.close();
// Si quieres reabrir. -> socket.connect();

Server Side

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) => {
    // ...
  });
});

Más en la documentación

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:

Nativo vs. Librerías

Ejemplos

Heroku

Heroku_logo

Documentación:

Uso

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

Addons

Otros paquetes

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

forever:

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

Awesome Node.js

Ejercicios

1 - Partiendo del repositorio simple-chat#template implementa toda la parte de servidor utilizando la librería socket.io.