1. Соглашение
WebSocket - это протокол, основанный на полной дуплексной связи между клиентами и серверами на TCP. Он определен в HTML5, а также является одной из основных характеристик нового поколения веб -приложений.
Он прорывается через ограничения более раннего AJAX, ключ-в режиме реального времени, и сервер может активно нажимать контент для клиента! Возможные приложения включают в себя: многопользовательские онлайн-игры, живой чат, мониторинг в реальном времени, удаленный рабочий стол, новостной сервер и т. Д.
Для меня то, что я хочу попробовать в данный момент, - это то, что можно сделать с комбинацией Canvas+WebSocket.
2. Реализация
Поскольку процесс рукопожатия является стандартным HTTP -запросом, существует два варианта реализации WebSocket: 1) реализации на TCP; 2) Реализация на существующем программном обеспечении HTTP. Последнее преимущество состоит в том, что он может делиться существующими портами HTTP-сервера и не нуждается в повторном внедрении функции аутентификации и анализе HTTP-запросов.
В этом примере используется HTTP -модуль узла. (См. Приложение для версии TCP и всех файлов)
1. Узел кода на стороне сервера:
var http = require ('http'); var url = require ('url'); // var mime = require ('mime'); var crypto = require ('crypto'); var port = 4400; var server = http.createserver ('); server.listen (port, function () {console.log ('Server работает на локальном host:', port); server .on ('connection', function (s) {console.log ('on connection', s);}) .on ('request', onrequest) .on ('revage', ontupgrade);}); var onrequest = request). Object.keys (req), req.url, req ['upgrade']); if (! req.upgrade) {// Выбор запроса не обновления: прерывать или предоставить обычную веб-страницу res.writehead (200, {'content-type': 'text/plain'}); res.write ('websocket server работает!'); } res.end (); return;}; var onupGrade = function (req, sock, head) {// console.log ('method:', object.keys (sock)); if (req.headers.upgrade! == 'websocket') {console.warn ('незаконное соединение'); sock.end (); возвращаться; } bind_sock_event (sock); try {Handshake (req, носок, голова); } catch (e) {console.error (e); sock.end (); }}; // обернуть кадр для отправки var wrood = function (data) {var fa = 0x00, fe = 0xff, data = data.tostring () len = 2+buffer.bytelength (data), buff = new Buffer (len); бафф [0] = fa; Buff.Write (данные, 1); Бафф [Len-1] = Fe; return buff;} // развернуть полученный кадр var unwrap = function (data) {return data.slice (1, data.length-1);} var bind_sock_event = function (sock) {sock .on ('data', function (buffer) {var data = unprap (buffer); console.log ('socke ateg Data:', buffer, data, data); Send ('Hello html5,'+date.now ()) sock.emit ('send', data); sock.write (wrap (data), 'Binary'); if (! Spaces) Thress {Сообщение: 'Неправильный ключ:'+КЛЮЧ, Имя: 'HandshakeError'} return get_big_endian (part / spaces);} var get_big_endian = function (n) {return string.fromChare.Apply (null, [3,2,0,0] .map (i) {return n >> 8*1). function (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', 'Upgrade: WebSocket', 'Connection: Upgrade', 'sec-websocket-Origin:' + h.origin, 'sec-websocket-location: ws: //' + h.host + req.url, 'sec-websocket-protocol: my-cust-chat-chat-chat-chat-prot-prot-prot-prot-prot-protocol. // body var c = Challenge (h ['sec-websocket-key1'], h ['sec-websocket-key2'], голова); output.push (c); sock.write (output.join (br), «двоичный»);}2. Код клиента браузера:
<html> <Head> <Title> Demo WebSocket </title> </head> <style type = "text/css"> textarea {width: 400px; высота: 150px; дисплей: блок; переполнение-Y: scroll;} #output {width: 600px; высота: 400px; founal: whitesmoke; Кнопка {padding: .2em 1em;} </style> <link href = "layout.css" rel = "styleSheet" type = "text/css"/> <body> <textarea id = "output" readonly = "readonly"> </textarea> <brse> </input "> <//textarea> </textarea> <br> </textarea id ="> <//textarea> <//button "<//id <//textare- 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 ('ate @'+new date (). tolocaletimestring ()+'/n'+e.data); output.scrolltop = output.scrollheight} socket.onclose = function (e) {log (e.type);} socket.addeventListener ('close', function () {log ('a worte into glose handler ..');}, false); // domvar id = function (id) {return document.getelementbyid (id); id ('input'), send = id ('send'); var log = function (msg) {output.textcontent += '>' +msg +'/n'} send.addeventlistener ('click', function () {socket.send (input.vale);}, false); </script> </body> </html>;3. Детали
Реализация протокола WebSocket выше протокола HTTP имеет только два шага: рукопожатие и отправка данных.
1. Потрясьте руки
Процесс рукопожатия называется испытанием-ответом. Во -первых, клиент инициирует запрос http get с именем обновления, сервер проверяет запрос, дает ответ 101, чтобы указать, что обновление протокола принимается, и рукопожатие завершено.
Информация о рукопожатии, украшенная Chrome Inspector:
URL -адрес запроса : WS: //192.168.144.131: 4400/pub/cat? Q = ME
Метод запроса: get
Код состояния: 101 Руководство протокола WebSocket
Запросить заголовки
Соединение: обновление
Хозяин: 192.168.144.131: 4400
Происхождение: 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
Обновление: WebSocket
(KEY3): 7C: 44: 56: CA: 1F: 19: D2: 0A
Заголовки ответов
Соединение: обновление
Sec-Websocket-Location: WS: //192.168.144.131: 4400/pub/chat? Q = ME
Sec-Websocket-Origin: http: // localhost: 800
Sec-Websocket-Protocol: My-Custom-Chat-протокол
Обновление: WebSocket
(Ответ на вызов): 52: DF: 2C: F4: 50: C2: 8E: 98: 14: B7: 7d: 09: CF: C8: 33: 40
Запросить заголовок
Хост: хост сервера WebSocket
Соединение: тип соединения
Обновление: тип обновления протокола
Происхождение: посетите источник
Sec-Websocket-Protocol: необязательное, подпротокол имени, определяемое самим приложением, и несколько протоколов делятся на пространства. (*Еще один оставшийся вариант - cookie)
Sec-Websocket-Key1: Ключ аутентификации безопасности, запрос XHR не может поддерживать заголовки запросов, начиная с «sec-».
Sec-websocket-key2: то же самое, что и выше
Ключ3: Содержание тела ответа, 8 байт случайных.
Заголовок ответа
Sec-Websocket-Protocol: должен содержать запрошенное имя субпротокола
Sec-Websocket-Origin: должен быть равен источнику запроса
Sec-Websocket-Location: должен быть равен запрошенному адресу
Ответ задания: содержание тела ответа, рассчитанное на основе трех ключей в запросе, 16 байтов.
Процесс расчета строки ответа псевдокод:
part_1 = все числа в Key1 / Количество пробелов в Key1 Part_2 То же, что и выше, sum = big_endian (part_1)+big_endian (part_2)+key3challenge_response = md5_digest (sum);
Стратегия расчета Big_endian для 32-битных целых чисел:
# Это очень похоже на расчет цвета RGBA. Из следующей функции мы видим, что процесс расчета 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. Отправить данные
API WebSocket разработан для обработки данных с использованием событий. Клиенты могут получить полные данные, если они получают уведомления о событиях без ручной обработки буфера.
В этом случае каждый данные называются кадром. В определении спецификации его голова должна начинаться с 0x00, а атрибут хвоста заканчивается 0xff, так что каждая отправка данных имеет как минимум два байта.
В реализации сервера головка и хвост должны быть отключены при получении данных; в то время как голова и хвост должны быть упакованы при отправке данных. Формат заключается в следующем:
# 'Hello' оригинальное двоичное представление, заголовок запроса и вот кодировки UTF8
<Буфер e4 bd a0 e5 a5 bd>
# Обернутое двоичное представление.
<Буфер 00 e4 bd a0 e5 a5 bd ff>
Выше приведено в этой статье, я надеюсь, что это будет полезно для каждого обучения.