1. Acuerdo
WebSocket es un protocolo basado en la comunicación dúplex completa entre clientes y servidores en TCP. Se define en HTML5 y también es una de las especificaciones básicas de la nueva generación de Apps.
Se rompe las limitaciones del AJAX anterior, la clave es en tiempo real y el servidor puede impulsar activamente el contenido al cliente. Las posibles aplicaciones incluyen: juegos en línea multijugador, chat en vivo, monitoreo en tiempo real, escritorio remoto, servidor de noticias, etc.
Para mí, lo que más quiero probar en este momento es lo que se puede hacer con la combinación Canvas+WebSocket.
2. Realización
Dado que el proceso de apretón de manos es una solicitud HTTP estándar, hay dos opciones para implementar WebSocket: 1) implementación en TCP; 2) Implementación en el software HTTP existente. La última ventaja es que puede compartir los puertos del servidor HTTP existentes y no necesita volver a implementar la función de autenticación y analizar las solicitudes HTTP.
El módulo HTTP del nodo se usa en este ejemplo. (Consulte el archivo adjunto para la versión TCP y todos los archivos)
1. Código del lado del servidor de nodo:
var http = require ('http'); var url = require ('url'); // var mime = require ('mime'); var crypto = require ('crypto'); var port = 4400; var servidor = http.createServer (); server.listen (puerto, function () {console.log ('Server se ejecuta en localhost:', port); servidor .on ('conexión', function (s) {console.log ('en conexión', s);}) .on ('request', onRequest) .on ('actualizar', oncupgrade); Var onrequest Object.Keys (req), req.url, req ['actualización']); if (! req.upgrade) {// Selección de solicitudes de no actualización: interrumpir o proporcionar una página web normal Res.Writehead (200, {'Content-type': 'Text/Plain'}); res.write ('WebSocket Server Works!'); } res.end (); return;}; var onUpgrade = function (req, sock, head) {// console.log ('método:', object.keys (sock)); if (req.headers.upgrade! == 'WebSocket') {console.warn ('conexión ilegal'); sock.end (); devolver; } bind_sock_event (calcetín); intente {handshake (req, calcetín, cabeza); } catch (e) {console.error (e); sock.end (); }}; // envuelve el marco que se enviará var wrap = function (data) {var fa = 0x00, fe = 0xff, data = data.ToString () len = 2+buffer.bytelength (data), buff = new Buffer (len); Buff [0] = fa; buff.write (datos, 1); Buff [Len-1] = Fe; return buff;}// Unwrap the received frame var unwrap = function(data) { return data.slice(1,data.length-1);}var bind_sock_event = function(sock) { sock .on('data',function(buffer) { var data = unwrap(buffer); console.log('socket receive data : ',buffer,data,'/n>>> '+data); // send ('Hello Html5,'+date.now ()) Sock.emit ('send', data); sock.write (wrap (data), 'binary'); if (? función (key1, key2, head) {var sum = get_part (key1) + get_part (key2) + head.ToString ('binary'); return crypto.createhash ('md5'). update (sum) .digest ('binary');} var handShake = function (req, sock, head) {var output = [], h = req.headers, br = '/r/n'; // Header Output.push ('Http/1.1 101 WebSocket Protocol HandShake', 'Actualade: WebSocket', 'Connection: Upgrade', 'Sec-Websocket-Origin:' + H.origin, 'sec-websocket --location: ws: //' + h.host + req.url ', sec-websocket-protocol: my-custom-chat-protocol-protocol-protocols' + bratocol ' + bratocol' + bratocol ' + bratocol' // cuerpo var c = desafío (h ['sec-websocket-key1'], h ['sec-websocket-key2'], cabeza); output.push (c); sock.write (output.Join (Br), 'binario');}2. Código del cliente del navegador:
<html> <fead> <title> websocket demo </title> </head> <style type = "text/css"> textArea {width: 400px; altura: 150px; display: block; overflow-y: scroll;} #output {width: 600px; altura: 400px; fondo: whitesmoke; almo botón {relleno: .2em 1em;} </style> <link href = "layout.css" rel = "stylesheet" type = "text/css"/> <body> <sextarea id = "output" readonly = "readonly"> </textarea> <br> <extarea Id = "input"> </textarea> <botón iD = "send" send "send" send "send" type = "text/javascript"> // localhostvar socket = new WebSocket ('ws: //192.168.144.131: 4400/') Socket.onopen = function (e) {log (e.type); Socket.send ('Hello Node');} Socket.OnClose = function (e) {log (e.type);} socket.onmessage = function (e) {log ('recibe @'+new Date (). output.scrolltop = output.scrollheight} Socket.OnClose = function (e) {log (e.type);} socket.adDeventListener ('cero', function () {log ('otro manejador de eventos Cerrar ..');}, falso); // DomVar Id = function (function (ID) {return Document.getElementByID (ID);} outereut ('outint (' outing ('outing'). = id ('input'), send = id ('send'); var log = function (msg) {output.TextContent += ''> ' +msg +'/n '} send.addeventListener (' click ', function () {Socket.send (input.value);}, falso); </script> </f/body> </html>3. Detalles
La implementación del protocolo WebSocket por encima del protocolo HTTP tiene solo dos pasos: apretón de manos y datos de envío.
1. Shoke Hands
El proceso de apretón de manos se llama desafío-respuesta. Primero, el cliente inicia una solicitud HTTP Get Solication con nombre de actualización, el servidor verifica la solicitud, proporciona una respuesta 101 para indicar que la actualización del protocolo se acepta y el apretón de manos se completa.
La información del apretón de manos embellecida por el inspector de Chrome:
URL de solicitud : WS: //192.168.144.131: 4400/pub/chat? Q = yo
Método de solicitud: obtener
Código de estado: 101 Shaka de manos del protocolo WebSocket
Solicitar encabezados
Conexión: actualización
Anfitrión: 192.168.144.131: 4400
Origen: http: // localhost: 800
SEC-Websocket-Key1: P2 G 947T 80 661 JAF2
Sec-Websocket-Key2: Z Zq ^326 5 9 = 7S1 1 7H4
Sec-websocket-protocol :: my-custom-chat-protocol
Actualización: WebSocket
(Key3): 7C: 44: 56: CA: 1f: 19: D2: 0a
Encabezados de respuesta
Conexión: actualización
Sec-Websocket-ubicación: ws: //192.168.144.131: 4400/pub/chat? Q = yo
Sec-Websocket-Origin: http: // localhost: 800
Sec-Websocket-Protocol: mi-chat-protocol
Actualización: WebSocket
(Respuesta del desafío): 52: DF: 2C: F4: 50: C2: 8e: 98: 14: B7: 7d: 09: cf: c8: 33: 40
Encabezado de solicitud
Host: Host del servidor WebSocket
Conexión: Tipo de conexión
Actualización: Tipo de actualización del protocolo
Origen: Visite la fuente
SEC-Websocket-Protocol: opcional, nombre de subprotocolo, definido por la aplicación misma, y múltiples protocolos se dividen por espacios. (*Otra opción restante son las cookies)
SEC-Websocket-Key1: Clave de autenticación de seguridad, solicitud de XHR no puede forjar encabezados de solicitud que comienzan con 'Sec-'.
Sec-websocket-key2: igual que el anterior
Key3: Respuesta contenido del cuerpo, 8 bytes aleatorios.
Encabezado de respuesta
SEC-Websocket-Protocol: debe contener el nombre de subprotocol solicitado
Sec-Websocket-Origin: debe ser igual a la fuente de la solicitud
Sec-Websocket-ubicación: debe ser igual a la dirección solicitada
Respuesta del desafío: el contenido del cuerpo de respuesta, calculado en base a las tres claves en la solicitud, 16 bytes.
Pseudocódigo del proceso de cálculo de cadena de respuesta:
Part_1 = Todos los números en Key1 / Número de espacios en Key1 Part_2 Igual que el anterior Sum = Big_endian (Part_1)+Big_endian (Part_2)+Key3Challenge_Response = MD5_Digest (sum);
Estrategia de cálculo Big_endian para enteros de 32 bits:
# Es muy similar al cálculo de color RGBA. De la siguiente función, podemos ver que el proceso de cálculo var big_endian = function (n) {return [3,2,1,0] .map (function (i) {return n >> 8*i & 0xff});} big_endian (0xcc77aaff); // -> [204, 119, 170, 255]]2. Enviar datos
La API de WebSocket está diseñada para procesar datos utilizando eventos. Los clientes pueden obtener datos completos siempre que reciban notificaciones de eventos sin procesar manualmente el búfer.
En este caso, cada datos se denomina marco. En la definición de especificación, su cabeza debe comenzar con 0x00 y el atributo de cola termina con 0xff, de modo que cada envío de datos tiene al menos dos bytes.
En la implementación del servidor, la cabeza y la cola deben cortarse al recibir datos; mientras que la cabeza y la cola deben enviarse al enviar datos. El formato es el siguiente:
# Representación binaria original 'Hola', el encabezado de solicitud y aquí son codificaciones UTF8
<Buffer E4 BD A0 E5 A5 BD>
# La representación binaria envuelta.
<Buffer 00 e4 bd a0 e5 a5 bd ff>
Lo anterior se trata de este artículo, espero que sea útil para el aprendizaje de todos.