Подробно о WebSocket — статьи Node

Node.js внешний интерфейс HTTP WebSocket
Подробный WebSocket - Node - Barret Lee - Blog Park HomeGithub личный блогWeiboподписка

Баррет Ли console.log( " Hi, I'm Barret, a Web Developer, try to be Excellent~ " );

войти в полноэкранный режим

Подробно о WebSocket — статьи Node

20.12.2013, 13:42, Баррет Ли, 22526 прочтений, 20 комментариев,собирать, редактировать

Поднято в предыдущем постеРазличные способы веб-коммуникации, в том числе и опросы, и средства для подключения давно упомянутого HTML5. В статье будет подробно описана реализация протокола WebSocket в веб-коммуникациях.

1. Протокол веб-сокета

1 Обзор

Протокол websocket позволяет ненадежному клиентскому коду управлять удаленными хостами в контролируемой сетевой среде. Протокол состоит из рукопожатия и базового кадрирования сообщения, наложенного на TCP. Проще говоря, после ответа на рукопожатие устанавливается безопасный информационный конвейер, который явно лучше, чемПреамбулаУпомянутый поток данных iframe на основе XMLHttpRequest и длительный опрос. Протокол включает в себя два аспекта: рукопожатие и передачу данных. передача).

2. Соединение с рукопожатием

Эта часть относительно проста, как приветствие знакомого на дороге.

Client:嘿,大哥,有火没?(烟递了过去)
Server:哈,有啊,来~ (点上)
Client:火柴啊,也行!(烟点上,验证完毕)

При рукопожатии клиент сначала активно связался:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Клиент сделал строку ключа шифрования Base64 в прошлом, который выше, вы можете увидеть ключ SEC-WebSocket. После просмотра клиентского сервера встретили тихо заявил клиенту, он уже знал, кстати, также скажу привет.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Сервер возвращает ответ Sec-WebSocket-Accept, и содержимое этого ответа формируется определенным образом. Алгоритм генерации такой:

mask  = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";  // 这是算法中要用到的固定字符串
accept = base64( sha1( key + mask ) );

После объединения ключа и маски они обрабатываются SHA-1, а обработанные данные снова шифруются с помощью Base64. Действие разложения:

1. t = "GhlIHNhbXBsZSBub25jZQ==" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
   -> "GhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
2. s = sha1(t) 
   -> 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 
      0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
3. base64(s) 
   -> "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

Код состояния HTTP, возвращенный сервером выше, равен 101. Если это не 101, это означает, что рукопожатие не удалось в начале ~

Вот демонстрация рукопожатия с сервером:

var crypto = require('crypto');

require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            // 握手
            // 应答部分,代码先省略
            console.log(e.toString());
        }else{

        };
    });
}).listen(8000);

Код клиента:

var ws=new WebSocket("ws://127.0.0.1:8000");
ws.onerror=function(e){
  console.log(e);
};
Запустите код

Выше, конечно, строка неполного кода, цель — продемонстрировать процесс рукопожатия, клиент приветствует сервер. В консоли видим:

Выглядит знакомо, но на самом деле отправляет HTTP-запрос, который мы также можем увидеть в сети браузера:

Однако протокол WebSocket не является протоколом HTTP. Заголовок HTTP был заимствован в начале проверки. После успешного подключения связь не является HTTP. Если вы мне не верите, попробуйте перехватить пакеты с помощью fiddler2 Соединение на основе TCP.

Для успешной связи с сервером должен быть ответ, см. ниже:

//服务器程序
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            //握手
            key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
            key = crypto.createHash('sha1').update(key + WS).digest('base64');
            o.write('HTTP/1.1 101 Switching Protocols\r\n');
            o.write('Upgrade: websocket\r\n');
            o.write('Connection: Upgrade\r\n');
            o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
            o.write('\r\n');
        }else{
            console.log(e);
        };
    });
}).listen(8000);

Про криптомодуль можно посмотретьофициальная документация, приведенный выше код должен быть хорошо понят. После ответа сервера клиент получает Sec-WebSocket-Accept, а затем выполняет локальную проверку. Если проверка проходит успешно, будет запущена функция onopen.

//客户端程序
var ws=new WebSocket("ws://127.0.0.1:8000/");
ws.onopen=function(e){
    console.log("握手成功");
};
запустить код

можно увидеть

3. Формат фрейма данных

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

При первом взгляде на эту картинку я боюсь, что меня вырвет кровью.Если университет пересматривает курс компьютерной сети, он не должен быть незнаком с этой вещью.Протокол передачи данных должен определять длину байта и связанные с ним значения.

FIN      1bit 表示信息的最后一帧,flag,也就是标记符
RSV 1-3  1bit each 以后备用的 默认都为 0
Opcode   4bit 帧类型,稍后细说
Mask     1bit 掩码,是否加密数据,默认必须置为1 (这里很蛋疼)
Payload  7bit 数据的长度
Masking-key      1 or 4 bit 掩码
Payload data     (x + y) bytes 数据
Extension data   x bytes  扩展数据
Application data y bytes  程序数据

Передача каждого кадра происходит по правилам этого протокола.Зная этот протокол, анализ не составит особого труда.Я возьму его непосредственно ниже.гипокарбонат кобальтакод одноклассника.

4. Разбор и кодирование кадров данных

Код синтаксического анализа для кадра данных:

function decodeDataFrame(e){
  var i=0,j,s,frame={
    //解析前两个字节的基本数据
    FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7,
    PayloadLength:e[i++]&0x7F
  };
  //处理特殊长度126和127
  if(frame.PayloadLength==126)
    frame.length=(e[i++]<<8)+e[i++];
  if(frame.PayloadLength==127)
    i+=4, //长度一般用四字节的整型,前四个字节通常为长整形留空的
    frame.length=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++];
  //判断是否使用掩码
  if(frame.Mask){
    //获取掩码实体
    frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]];
    //对数据和掩码做异或运算
    for(j=0,s=[];j<frame.PayloadLength;j++)
      s.push(e[i+j]^frame.MaskingKey[j%4]);
  }else s=e.slice(i,frame.PayloadLength); //否则直接使用数据
  //数组转换成缓冲区来使用
  s=new Buffer(s);
  //如果有必要则把缓冲区转换成字符串来使用
  if(frame.Opcode==1)s=s.toString();
  //设置上数据部分
  frame.PayloadData=s;
  //返回数据帧
  return frame;
}
decodeDataFrame Function

Кодирование кадра данных:

//NodeJS
function encodeDataFrame(e){
  var s=[],o=new Buffer(e.PayloadData),l=o.length;
  //输入第一个字节
  s.push((e.FIN<<7)+e.Opcode);
  //输入第二个字节,判断它的长度并放入相应的后续长度消息
  //永远不使用掩码
  if(l<126)s.push(l);
  else if(l<0x10000)s.push(126,(l&0xFF00)>>2,l&0xFF);
  else s.push(
    127, 0,0,0,0, //8字节数据,前4字节一般没用留空
    (l&0xFF000000)>>6,(l&0xFF0000)>>4,(l&0xFF00)>>2,l&0xFF
  );
  //返回头部分和数据部分的合并缓冲区
  return Buffer.concat([new Buffer(s),o]);
}
encodeDataFrame Function

Некоторая детская обувь может не понимать, какие данные следует анализировать. Эта задача синтаксического анализа в основном обрабатывается сервером, а данные, отправляемые клиентом, имеют форму двоичного потока, например:

var ws = new WebSocket("ws://127.0.0.1:8000/"); 
ws.onopen = function(){
  ws.send("握手成功");
};
запустить код

Информация, полученная сервером, выглядит следующим образом:

Бинарный поток в формате Buffer. И проанализируйте этот двоичный поток, когда мы выводим:

//服务器程序
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            //握手
            key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
            key = crypto.createHash('sha1').update(key + WS).digest('base64');
            o.write('HTTP/1.1 101 Switching Protocols\r\n');
            o.write('Upgrade: websocket\r\n');
            o.write('Connection: Upgrade\r\n');
            o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
            o.write('\r\n');
        }else{
            // 输出之前解析帧
            console.log(decodeDataFrame(e));
        };
    });
}).listen(8000);

На выходе объект с очень четкой информацией о кадре:

5. Контроль соединений

Купил пропуск выше, опкод указан, подробного описания нет,официальная документацияТакже дает таблицу:

 |Opcode  | Meaning                             | Reference |
-+--------+-------------------------------------+-----------|
 | 0      | Continuation Frame                  | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 1      | Text Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 2      | Binary Frame                        | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 8      | Connection Close Frame              | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 9      | Ping Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 10     | Pong Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|

decodeDataFrame анализирует данные, и результирующий формат данных:

{
    FIN: 1,
    Opcode: 1,
    Mask: 1,
    PayloadLength: 4,
    MaskingKey: [ 159, 18, 207, 93 ],
    PayLoadData: '握手成功'
}

Затем вы можете просмотреть его в соответствии с приведенным выше.Функция этого фрейма заключается в отправке текста, который является текстовым фреймом. Поскольку соединение основано на TCP, если вы напрямую закроете TCP-соединение, канал будет закрыт. Однако дизайн WebSocket более удобен для пользователя. Перед закрытием я передам вам привет. На стороне сервера вы можно судить об опкоде кадра:

var frame=decodeDataFrame(e);
console.log(frame);
if(frame.Opcode==8){
    o.end(); //断开连接
}

Формат данных (фреймов) взаимодействия между клиентом и сервером одинаков, пока клиент отправляетws.close(), сервер выполнит описанную выше операцию. И наоборот, если сервер отправляет тот же закрытый кадр клиенту:

o.write(encodeDataFrame({
    FIN:1,
    Opcode:8,
    PayloadData:buf
}));

Клиент будет включать соответствующие функции, такие взаимодействия по-прежнему правила имеют моменты, не склонны к ошибке.

2. Вопросы, требующие внимания

1. WebSocket URIs

Многие люди могут знать толькоws://text.com:8888, но на самом деле адрес протокола веб-сокета может добавить путь и запрос.

ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

Если используется протокол wss, URI будет подключен безопасным образом. Здесь wss не чувствителен к регистру.

2. "Лишняя" часть соглашения (Tucao)

Запрос рукопожатия содержит поле Sec-WebSocket-Key, которое может быть замечено проницательным человеком как подключение по вебсокету, и метод шифрования этого поля также фиксируется на сервере.Если кто-то захочет вас взломать, он не будет слишком трудно.

Затем есть маска MaskingKey, так как она принудительно шифруется (Mask is 1 означает шифрование, а метод шифрования — обработка XOR между MaskingKey и PayLoadData), нужно ли разработчикам разбираться с этой штукой? Разве не было бы достаточно инкапсулировать его прямо внутри?

3. Связь с TCP и HTTP

Протокол WebSocket является протоколом на основе TCP, то есть, когда ссылка рукопожатия связана с HTTP (отправляется HTTP-запрос), сервер переключает запрос на протокол веб-сокета (обновление). websocket использует порт 80 в качестве порта подключения к веб-сокету по умолчанию, а веб-сокет работает на порту 443.

3. Ссылки

4. Отдельное спасибо

Спасибо еще разгипокарбонат кобальтаРазговаривал со мной несколько часов :), часть кода узла в этой статье взята из его блога.

 

В следующий раз я буду использовать php в качестве фона, чтобы объяснить соответствующие знания о веб-сокетах.

Подробности о WebSocket-php

 

 

награда

Уведомление об авторских правах: Attribution-Non-Commercial-No Derivatives 3.0 International (CC BY-NC-ND 3.0)

Я тоже скажу пару слов↓

  1. #1-й этаж simonleung   2013-12-20 14:31
    websocket начинает рукопожатие с событием http onupgrade
    Не использовать ondata сети.
    узел будет .org/API/HTTP. Контракт... Поддержка(0) против (0)
  2. #2-ой этаж[арендодатель] Barret Lee   2013-12-20 14:37
    @ simonleung
    Данные сети находятся на более низком уровне, чем при обновлении http.
    От описания протокола вебсокета до отображения результатов теста нет никаких проблем.Поддержка(0)против (0) http://pic.cnblogs.com/face/387325/20150805014702.png
  3. #3-й этаж Nima Fan Ye.   2013-12-20 21:21
    Очень хорошо написано, восхищаюсьПоддержка(0)против (0) http://pic.cnblogs.com/face/395638/20140818132358.png
  4. #4-й этаж логический   2013-12-20 22:04
    Я не знаю, почему, когда я использую node-webkit, но соединение очень медленное, когда количество соединений больше 100 (более 100).Поддержка(0)против (0) http://pic.cnblogs.com/face/u17705.jpg
  5. #5-й этаж второй брат Тони   2013-12-21 11:56
    ищу эту статьюПоддержка(0)против (0) http://pic.cnblogs.com/face/348990/20160120104927.png
  6. #6 этаж Ace8793   2013-12-22 22:00
    mark Поддержка(0)против (0) http://pic.cnblogs.com/face/377458/20161107194642.png
  7. #7 этаж flyher   2013-12-23 08:26
    @BarretLee
    Хорошая статья, увидела только в понедельник, реально поздно видеться, сохраните на будущее.Поддержка(0)против (0) http://pic.cnblogs.com/face/u218282.jpg
  8. №8 Этаж dotNetDR_   2013-12-24 15:26
    хорошо~Поддержка(0)против (0) http://pic.cnblogs.com/face/u59691.jpg
  9. №9 Этаж исчисление г   2013-12-26 21:07
    Отлично, выучил!Поддержка(0)против (0)
  10. №10 этаж это Смурфики   2014-03-26 20:08
    УчитьПоддержка(0)против (0)
  11. №11 Этаж титульный лист   2014-04-02 10:54
    Уважаемый, картинаПоддержка(0)против (0)
  12. #12F[арендодатель] Barret Lee   2014-04-02 16:38
    @титульный лист
    Я в порядке здесь, вы можете обновить его?Поддержка(0)против (0) http://pic.cnblogs.com/face/387325/20150805014702.png
  13. №13 Этаж mcarzx   2014-04-21 17:46
    1. t = "GhlIHNhbXBsZSBub25jZQ==", в начале строки отсутствует буква d
    2. console.log(crypto.createHash('sha1').update('dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('sha1')); // Вывод: , в отличие от вашего, с 0x впереди, как вы его получили?

    3. Часть декодирования: if(frame.PayloadLength==126) frame.length=(e[2]
    4, кодирующая часть: s.push (126, (l & 0xff00) >> 2, L & 0xFF); Где >> 2, кажется >> 8. Следующая строка аналогична.Поддержка(0)против (0)
  14. № 14 этаж Маста   2014-11-12 21:03
    Простите, брат усат, как postMessage общается между доменами, онлайн-поиск сложен, но я в этом не разбираюсьПоддержка(0)против (0)
  15. №15 Этаж[арендодатель] Barret Lee   2014-11-12 21:51
    @Маста
    Страница A взаимодействует с iframe B, который является чужим для страницы.
    И postMessage, и onmessage запускаются для одного и того же оконного объекта.

    Получите дескриптор окна B в A, используйте B.contentWindow.postMessage для отправки информации в B, а B имеет прослушиватель onmessage.

    Принцип отправки сообщения от b до a это то, что б Получите ручку окна, используйте a.parent.postmessage для отправки информации в A, а A имею прослушиватель OnMessage.

    Очистить принцип, он должен быть одинаковым окном для отправки информации для себя, но мы получаем дескриптор окна в другом окне, например iframe.contentwindow, или iframe получает окно окна службы.

    Если вы этого не понимаете, это не потому, что у вас проблемы с пониманием, а потому, что вы недостаточно хорошо поняли эти понятия. Если вы все еще не понимаете, прочтите сами MSDN/MDN. документация.Поддержка(0)против (0) http://pic.cnblogs.com/face/387325/20150805014702.png
  16. №16 Этаж riskers   2015-03-04 14:24
    После объединения ссылочного ключа и маски они обрабатываются SHA-1, а обработанные данные снова шифруются с помощью Base64.

    base64 - это метод кодирования, а не шифрованиеПоддержка(0)против (0)
  17. №17 Этаж CoderZ   2015-06-29 12:05
    Простите, брат усатый, только что понял, что ваш метод удобен в применении. Я снова посмотрел на socket.io, но обнаружил, что в заголовке запроса socket.io нет информации о веб-сокете, но он основан на веб-сокете, я не знаю, почему?Поддержка(0)против (0) http://pic.cnblogs.com/face/579305/20131227132708.png
  18. №18 Этаж Пинавший Человек.Орз   2015-07-20 15:55
    mark Поддержка(0)против (0) http://pic.cnblogs.com/face/520225/20131008114345.png
  19. №19 Этаж gongmaolan123   2017-01-13 12:59
    GoEasy web: три шага для простой реализации push-уведомлений в реальном времени
    1. Представьте goeasy.js

    2. Клиентская подписка,
    Var goeasy = new GoEasy({ключ приложения:'ваш ключ приложения'});
    goousy.subscribe (канал: "Ваш канал", OnMessage: Функция (сообщение)
    {оповещение('полученное сообщение'+ message.content)}
    )

    3. Три метода нажатия
    Javascript: goeasy.publish({channel:'ваш канал', message:'ваше сообщение о публикации'});

    Java SDK:
    GoEasy goeasy = новый GoEasy("ключ приложения");
    goeasy.publish("ваш канал", "ваше сообщение");

    RestAPI: полегче.IO/полегче/водопад…
    三步轻松实现web推送及接收。 Официальный сайт:goeasy.io, документация завершенаПоддержка(0)против (0)
  20. #20F38077282017/10/11 18:57:09 линукс,   2017-10-11 18:57
    @ simonleung
    Модуль http написан на основе сети, а в сети нет этого события.Поддержка(0)против (0)
обновить комментарийобновить страницу Back to topЗарегистрированные пользователи могут оставлять комментарии только после авторизации.Авторизоватьсяилирегистр,доступдомашняя страница сайта.[Рекомендуется] 500 000 строк исходного кода VC++: крупномасштабное промышленное управление конфигурацией, моделирование энергопотребления САПР и библиотека исходного кода ГИС
【Событие】Alibaba Cloud Double 11 Event начинает разогрев облачного сервера со скидкой 20% в течение ограниченного времени
[Расследование] Немедленно примите участие в отмеченном призом исследовании, вы на самом деле программист Цзянцзы!
[Рекомендуется] Быстрый старт Vue.js 2.x, множество эффективных практических примеров
葡萄城 Последние ИТ-новости:
· Рост технического образования
· Верховный суд США отклонил апелляцию Samsung и должен выплатить Apple 120 миллионов долларов в качестве компенсации
· Черная технология Adobe выглядит более химической, чем таблица химических элементов
· Дисбаланс в индустрии экспресс-доставки: штраф за условное депонирование приводит к нехватке рабочей силы, новички берутся за работу без обучения
· Почему хорошая репутация мобильных телефонов Sony не может принести хороших продаж?
» больше новостей... 阿里云双11 Последние статьи базы знаний:
· 3+10 привычек, улучшающих качество жизни программиста
· 10 принципов кодирования НАСА
· Почему ты проходишь так много тренировок и все равно остаешься посредственным?
· Письмо начинающему фронтенд-инженеру
· Практические принципы проектирования виртуального частного облака VPC
» Другие статьи базы знаний...

Переезд блога: http://barretlee.com

Barret Lee шарф:@Barret, Ли Цзин, усы Brother Brother Channel общественное число:усатый брат😃
Гитхаб:barretlee
Статус: Taobao UED-F2E и медленные осадки
Отказ от ответственности: Все статьи без специальных указаний являются оригинальными.Поскольку старый и неправильный контент часто модифицируется или удаляется, пожалуйста, указывайте адрес источника для перепечатки. Статьи являются личным мнением и не представляют компанию. ты первый网页访问计数器посетитель, добро пожаловать~ Никнейм:Barret Lee
Возраст сада:5 лет 7 месяцев
честь:Рекомендуемый блог
вентилятор:2731
Сфокусируйся на:20 +Добавить подпискуПоиск статьи: Go!

последнее эссе

последний комментарий

Архив эссе

javascript

календарь

< ноябрь 2017 г. >
день один два три Четыре Пятерки шесть
29 30 31 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 1 2
3 4 5 6 7 8 9

классификация эссе

Рекомендуемая таблица лидеров

Читать таблицу лидеров

css

friends

Tools

готов сделать ~

  • Изучаем интерфейсную архитектуру
  • Исследование ECMAScript6
  • Ecma-262 Peruse
Недавно в... рекламировать
  • Стажер-стажер Taobao UED, где ваше резюме? Отправьте его сюда на barret.china@gmail.com~
  • Совсем недавноМикрообмен
исходный код | Обратная связь
  • ток201Общий чат 0
Примечание: журнал чата не будет сохранен, обратите внимание на резервную копию Совет: нажмите на аватарку, чтобы войти в приватный чатВыше это реклама, давайте поговорим. Отправить Возьмите имя:подтверждать