Появление HTML5 знаменует собой коллективную вспышку различных современных браузеров в эпоху после Flash.Это также должно остерегаться доминирующих производителей Adobe.После многих лет борьбы друг с другом они хотят изменить свой образ жизни и, наконец, прийти к определенному консенсусу. , Концентрированный выпуск технологии као усталого бэя - так называемая "Н5 - это корзина, в нее можно положить все".
На этот раз мы обсудим один из наиболее заметных и широко поддерживаемых пунктов.WebSocket. В этой статье мы попытаемся объяснить, для решения какой проблемы он используется, и чем он похож на закаленный в боях «традиционный» Socket.
I. Определение и происхождение
В лице имени WebSocket,web
Не надо лишнего объяснять, это тупо непонятноsocket
Выглядит довольно знакомо, неважно, есть ли какая-то связь, можно сначала познакомиться:
(1.1) API традиционных сокетов
Сокет часто относится к двум концам соединения в сетевой среде TCP/IP и набору программных API, предназначенных для облегчения такой разработки.
Как показано на рисунке, буквальное значение английского слова «socket» — «отверстие» или «гнездо».
Как технический термин,socket
Обычно принимают последнее значение, как пористая розетка. Он используется для описания IP-адресов и портов на обоих концах канала связи и может использоваться для реализации связи между различными устройствами.Socket
,TCP Socket
Все они являются общепринятыми именами, и с китайского обычно переводятся как **«сокет», «сокет TCP»** и так далее.
... Насчет того, почему "сокет" переводится как "сокет", любопытных программистов немало.Статью научного исследования можно найти по ссылке внизу статьи.
Подумайте о хост-сервере как о комнате, полной розеток, каждая из которых имеет номер, некоторые обеспечивают 220 вольт переменного тока, некоторые обеспечивают сигналы стационарной связи, а некоторые обеспечивают программы кабельного телевидения. Клиентское программное обеспечение подключает вилки к розеткам с разными номерами для получения разных услуг.
Этаж, на котором находится Socket API
Модель OSIВ качестве концептуальной модели, предложенной Международной организацией по стандартизации (ISO), стандартная структура пытается объединить различные компьютеры в сеть по всему миру. Все известные нам протоколы, такие как HTTP и FTP, работают на верхнем прикладном уровне (прикладной уровень).
**Набор протоколов TCP/IP (Protocol Suite)** абстрагирует процесс связи программного обеспечения на четыре уровня абстракции, которые часто рассматриваются как упрощенная семиуровневая модель OSI. Когда несколько уровней протоколов работают вместе, это похоже на стек в структуре данных, поэтому его также называютСтек протоколов TCP/IP (стек протоколов).
Когда речь идет только о TCP, он относится к протоколу управления передачей, ориентированному на соединение. После TCP-соединения клиент и сервер могут отправлять и получать сообщения друг другу.Соединение существует до тех пор, пока клиент или сервер не отключаются активно, поэтому оно называется длинным соединением.
Сокет на самом деле не является стандартным протоколом, а уровнем абстракции промежуточного программного обеспечения для связи между прикладным уровнем и семейством протоколов TCP/IP.Это набор интерфейсов, и его рабочее положение в основном находится на уровне сеанса (уровень 5) Модель OSI Это для удобства всех Уровень абстракции, который существует непосредственно с использованием протокола более низкого уровня (обычно TCP или UDP).
В режиме разработки Socket на самом деле является фасадным режимом, который скрывает сложное семейство протоколов TCP / IP за Socket API.Для пользователей набор простых интерфейсов - это все, позвольте Socket организовать данные в соответствии с указанным протоколом.
Самый ранний набор Socket API был реализован на языке C, который стал де-факто стандартом Socket.
Общие реализации API сокетов
Реализация на некоторых языках
Традиционные back-end языки программирования в основном имеют инкапсуляцию Socket API; до появления HTML5, если вы хотите использовать чистую front-end технологию для реализации клиента TCP Socket, в основном есть только Java Applet (java.net.Socket
илиjava.net.DatagramSocket
илиjava.net.MulticastSocket
), Вспышка (flash.net.Socket
илиflash.net.XMLSocket
) или Сильверлайт (System.Net.Sockets
) и т. д. можно выбрать.
Давайте возьмем серверную/клиентскую реализацию PHP в качестве примера, чтобы продемонстрировать самый простой пример:
<?php
//server.php
set_time_limit(0);
$ip = '127.0.0.1';
$port = 1999;
// 创建一个Socket
$sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
// 绑定Socket地址和端口
$ret = socket_bind($sock,$ip,$port);
// 开始监听链接
$ret = socket_listen($sock,4);
$count = 0; //最多接受几次请求后就退出
do {
// 另一个Socket来处理通信
if (($msgsock = socket_accept($sock)) >= 0) {
// 发到客户端
$msg ="server: HELLO CLIENTS!\n";
if (socket_write($msgsock, $msg, strlen($msg))) {
echo "发送成功!\n";
}
// 获得客户端的输入
$buf = socket_read($msgsock,8192);
$talkback = "接受成功!内容为:$buf\n";
echo $talkback;
if(++$count >= 5){
break;
};
}
// 关闭sockets
socket_close($msgsock);
} while (true);
socket_close($sock);
echo "TCP 连接关闭OK\n";
?>
<?php
//client.php
error_reporting(E_ALL);
set_time_limit(0);
$port = 1999;
$ip = "127.0.0.1";
// 创建Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 绑定Socket地址和端口
$result = socket_connect($socket, $ip, $port);
if ($result >= 0) echo "TCP 连接OK\n";
$in = "client: HELLO SERVER!\r\n";
if(socket_write($socket, $in, strlen($in))) {
echo "发送成功!\n";
}
$out = '';
while($out = socket_read($socket, 8192)) {
echo "接受成功!内容为:",$out;
}
socket_close($socket);
echo "TCP 连接关闭OK\n";
?>
(1.2) Протокол WebSocket, представленный HTML5
* сеть + сокет? *
WebSockets
Он обеспечивает возможность интерактивной связи в реальном времени для обоих концов C/S, позволяя серверу активно отправлять информацию клиенту, что представляет собой новый двусторонний поток данных, отличный от HTTP.протокол
Проще говоря, традиционный TCP-сокет представляет собой набор относительныхСтандартизированный API, а WebSocket, появившийся совсем недавно, — это своего родаСетевой протокол-- Две разные вещи.
Нижний уровень WebSocket основан на протоколе TCP, поэтому в ранней версии он назывался TCPConnection.
Socket 之于 WebSocket
就像
Java 和 JavaScript
雷锋 和 雷峰塔
张三 和 张三丰
周杰 和 周杰伦
北大 和 北大青鸟
印度 和 印度尼西亚
一样
基本上没啥关系...
-- 鲁迅《我没说过的话》
проблема, которую нужно решить
* Как работает HTTP *При HTTP/HTTPS на основе режима запрос/ответ, если это сценарий с высокими требованиями к реальному времени, клиенту необходимо постоянно запрашивать у сервера доступные данные, что неуклюже и нерентабельно во всех аспектах.
*Как работают веб-сокеты*В полнодуплексном режиме WebSocket (позволяющем передавать данные в обоих направлениях одновременно) клиенту нужно только тихо прослушать приветствие, а сервер автоматически уведомит его, когда появятся доступные данные.
Технологии, аналогичные WebSocket
На самом деле, когда дело доходит до реального временидвусторонняя связьКогда возникнет проблема, мы, естественно, подумаем о некоторых попытках, основанных на технологии HTTP на протяжении многих лет; именно на основе практики и проблем в этих предыдущих работах появился WebSocket. Кратко рассмотрим соответствующие схемы и их подводные камни:
Опрос
С помощью setInterval() и других методов клиент постоянно отправляет запросы и получает ответы. Этот подход относительно прост и может решить проблему в определенной степени. Тем не менее, интервал опроса должен быть тщательно продуман. Если интервал опроса слишком длинный, пользователи не смогут вовремя получать обновленные данные, если интервал опроса слишком короткий, это вызовет слишком много запросов и увеличит нагрузку на сервер.
Длинный опрос
Это улучшение по сравнению с опросом. После того, как клиент отправляет запрос, сервер блокирует запрос с помощью while(true) и других методов и отправляет данные ответа до тех пор, пока не появятся доступные данные, а клиент отправляет следующий запрос после получения ответа.
Этот метод также называется «Висячий GET», «Обратный Ajax» или «Комета» и т. д. Хотя он выглядит как проталкивание сервера, это все же медленный ответ на основе HTTP, а в случае частых обновлений данных его эффективность не лучше общего опроса.
Потоковое HTTP (потоковое)
Используйте HTTP 1.1, а заголовок ответа содержитTransfer-Encoding: chunked
В случае , данные, отправляемые сервером клиенту, могут быть разделены на несколько частей, оставлены открытыми (пока истина, спящий режим и т. д.) и периодически сбрасывать данные (фрагментами).
Клиент отправляет только одно HTTP-соединение. В состоянии xhr.readyState==3 используйте xhr.responseText.substring для получения всех данных.
Однако ответы данных могут быть задержаны посредниками, такими как прокси-серверы или брандмауэры, поэтому может потребоваться дополнительное исследование этой ситуации, чтобы переключиться на длительный опрос.
SSE (Server-Sent Events)
Спецификация SSE также является неотъемлемой частью спецификации HTML 5. Тип содержимого ответа на стороне сервера:text/event-stream
, используйте на стороне браузераEventSource
Объект обрабатывает возвращенные данные.
По сравнению с WebSocket недостатками SSE являются:
- КОРС не поддерживается
- Односторонний канал, только сервер может отправить в браузер
- Совместимость с браузером немного плохая
Где пригодятся веб-сокеты
Большинство традиционных методов не только тратят впустую пропускную способность (HTTP HEAD относительно велик), но также потребляют ресурсы ЦП сервера (запросы должны приниматься без информации); в то время как WebSocket значительно снижает вышеуказанное потребление и больше подходит для следующих сценариев:
- Высокое применение требований в реальном времени
- чат
- IoT (Интернет вещей — Интернет вещей)
- онлайн мультиплеер
Совместимость также удовлетворительна.Если вам нужно сказать, когда это неприменимо, это, вероятно, несколько случаев, когда необходима совместимость со старыми браузерами или требования к реальному времени явно не высоки.
Расширения для HTTP
URL-адрес для соединений WebSocket для использованияws://
илиwss://
и т. д., его шифрование, файлы cookie и другие политики в основном такие же, как и у соответствующих HTTP/HTTPS.
Протоколы прикладного уровня, такие как HTTP и WebSocket, основаны на протоколе TCP для передачи данных.Эти протоколы высокого уровня можно понимать как инкапсуляцию TCP.
В HTTP, если клиент не отправляет запрос, сервер никогда не сможет отправить данные клиенту.
Для WebSocket он должен полагаться на протокол HTTP, чтобы рукопожатие было совместимо со спецификациями браузера; его можно рассматривать как дополнение и обновление HTTP.
После выполнения первого HTTP-запроса последующая двусторонняя связь осуществляется по TCP-каналу. Следовательно, хотя HTTP и WebSocket основаны на протоколе TCP, это два совершенно разных метода связи.
Давайте посмотрим на типичный запрос рукопожатия WebSocket:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Ответ сервера:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Первый ключевой момент заключается в том, чтоUpdate
а такжеConnection
Заголовок ответа на запрос, указывающий, что запрос и подтверждение переключаются на протокол WebSocket;
13
фиксированный номер версии;
а заголовок запросаSec-WebSocket-Key
Кодировка Base64, случайно сгенерированная браузером, дополняет ее фиксированным258EAFA5-E914-47DA-95CA-C5AB0DC85B11
«Волшебная строка», затем вычислите дайджест SHA-1 и кодировку BASE-64, которая является ответомSec-WebSocket-Accept
. ╮(╯▽╰)╭
Реализация в браузере
Можно вызывать прямо в браузереWebSocket
объект, который определяется следующим образом:
enum BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
readonly attribute USVString url;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long long bufferedAmount;
// networking
attribute EventHandler onopen;
attribute EventHandler onerror;
attribute EventHandler onclose;
readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
void close([Clamp] optional unsigned short code, optional USVString reason);
// messaging
attribute EventHandler onmessage;
attribute BinaryType binaryType;
void send(USVString data);
void send(Blob data);
void send(ArrayBuffer data);
void send(ArrayBufferView data);
};
Его можно использовать так:
var ws = new WebSocket('ws://www.xxx.com/some.php');
ws.send('xxx'); //每次只能发送字符串
ws.onmessage = function(event) {
var data = event.data;
};
ws.onerror = function() {
ws.close();
};
II. Экземпляр WebSocket для многопользовательского взаимодействия
Просто представьте себе пользовательский сценарий: например, если мы хотим сделать карточную онлайн-игру, она должна быть в виде нескольких людей, входящих в одну и ту же комнату, и действия каждого человека могут транслироваться другим.
Давайте воспользуемся WebSocket для создания базового прототипа проверки, чтобы каждый игрок знал о входе, выходе, игре, раскаянии и даже жульничестве других людей:
(2.1) Реализация на стороне сервера
Мы используем nodejs+expressjs для создания базового сервера и реализуем серверную логику протокола WebSocket с библиотекой, упакованной https://github.com/websockets/ws:
// server.js
var express = require('express')
var ws = require('./ws')
var app = express()
app.get('/', function (req, res) {
res.sendFile(__dirname + '/ws.html');
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
// ws.js
const { Server, OPEN } = require('ws');
const clients = []; //array of websocket clients
const cardsArr = []; //array of {cardId, count, title, ...}
let _lock = false;
const wss = new Server({port: 40510})
wss.on('connection', function (ws) {
const _cid = clients.push(ws) - 1;
ws.on('message', function (json) {
const {
act,
cid,
data
} = JSON.parse(json);
switch (act) {
case 'client:join':
_onCustomerJoin(ws, _cid);
break;
case 'client:leave':
_onCustomerLeave(cid);
break;
case 'client:add': //增加牌
_onAddCard(cid, data);
break;
case 'client:update': //修改牌
_onUpdateCard(cid, data);
break;
case 'client:remove': //删除牌
_onRemoveCard(cid, data);
break;
case 'client:win': //下单
_onWin(cid);
break;
default:
console.log('received: %s', act, cid)
break;
}
});
});
function _ensureLock(func) {
return function() {
if (_lock) return;
_lock = true;
const rtn = func.apply(null, arguments);
_lock = false;
return rtn;
};
}
function _findCard(cardId) {
const cidx = cardsArr.map(Card=>Card.cardId).indexOf(cardId);
return cidx;
}
const _broadcast = (excludeId, msg, data=null)=>{
clients.forEach( (client, cidx)=>{
if (cidx === excludeId) return;
if (client && client.readyState === OPEN) {
client.send(JSON.stringify({
act: 'server:broadcast',
msg: msg,
data: data
}));
}
} );
};
const _onCustomerJoin = (ws, cid)=>{
ws.send(JSON.stringify({
act: 'server:regist',
data: {
cid: cid
}
}));
_broadcast(cid, '玩家加入:', {cid: cid});
};
const _onCustomerLeave = (cid)=>{
clients[cid].terminate();
clients.splice(cid, 1);
_broadcast(cid, '玩家退出:', {cid: cid});
};
const _onAddCard = _ensureLock( (cid, data)=>{
const d = _findCard(data.cardId);
if (d !== -1) {
cardsArr.splice(d, 1);
}
cardsArr.push(data);
_broadcast(-1, '玩家添加了牌', {
cid: cid,
Card: data,
cardsArr: cardsArr
});
} );
const _onUpdateCard = _ensureLock( (cid, data)=>{
const d = _findCard(data.cardId);
if (d === -1) return;
cardsArr[d] = data;
_broadcast(-1, '玩家更改了牌', {
cid: cid,
Card: data,
cardsArr: cardsArr
});
} );
const _onRemoveCard = _ensureLock( (cid, data)=>{
const d = _findCard(data.cardId);
if (d === -1) return;
cardsArr.splice(d, 1);
_broadcast(-1, '玩家删除了牌', {
cid: cid,
Card: data,
cardsArr: cardsArr
});
} );
const _onWin = _ensureLock( (cid)=>{
//do sth. here
_broadcast(cid, '玩家胡牌了');
} );
(2.2) Реализация клиента
<h1></h1>
<div></div>
<button onclick="_add()">出牌</button>
<button onclick="_update()">换牌</button>
<button onclick="_remove()">悔牌</button>
<button onclick="_win()">胡牌</button>
<button onclick="_leave()">离开</button>
<script>
let cid = null;
const ws = new WebSocket('ws://localhost:40510');
ws.onopen = function () {
console.log('websocket is connected ...');
_send({
act: 'client:join'
});
};
ws.onmessage = function (ev) {
const {
act,
msg,
data
} = JSON.parse(ev.data);
switch(act) {
case 'server:regist':
cid = data.cid;
console.log(`regist: cid is ${cid}`);
document.querySelector('h1').innerHTML = 'I AM: ' + cid;
break;
case 'server:broadcast':
console.log('从服务器端接收的广播:', msg, data);
if (data && data.cardsArr) {
document.querySelector('div').innerHTML = JSON.stringify(
data.cardsArr, null, 4
);
}
break;
default:
console.log(ev);
break;
}
}
function _send(json) {
ws.send(JSON.stringify(json));
}
function _add() {
_send({
act: 'client:add',
cid: cid,
data: {
cardId: 111,
count: 1,
title: '红桃A'
}
})
}
function _update() {
_send({
act: 'client:update',
cid: cid,
data: {
cardId: 111,
count: 2,
title: '黑桃9'
}
})
}
function _remove() {
_send({
act: 'client:remove',
cid: cid,
data: {
cardId: 111
}
})
}
function _win() {
_send({
act: 'client:win',
cid: cid
})
}
function _leave() {
_send({
act: 'client:leave',
cid: cid
})
}
</script>
(2.3) Эффект операции
Игрок 0 присоединяется:
Игрок 1 присоединяется:
Игрок 1 играет:
Игрок 1 останавливается и уходит:
III. Резюме
- Традиционный сокет TCP часто относится к двум концам соединения в сетевой среде TCP/IP и набору программных API, предназначенных для облегчения такой разработки.
- WebSockets обеспечивает возможность интерактивной связи в реальном времени между концами C/S, позволяя серверу активно отправлять информацию клиенту.
- WebSockets, неотъемлемая часть спецификации HTML 5, представляет собой новый двунаправленный протокол потоковой передачи данных, отличный от HTTP.
- Веб-сокеты с полнодуплексной связью эффективно устраняют недостатки предыдущих методов, таких как длительный опрос.
- Веб-сокеты подходят для приложений с высокими требованиями к реальному времени, чатов, многопользовательских игр и т. д.
IV. Ссылки
- Зачем переводить на сокеты: https://www.bbsmax.com/A/kvJ3rDV9zg/
- https://blog.zengrong.net/post/2199.html
- http://www.cnblogs.com/thinksasa/archive/2013/02/26/2934206.html
- https://coderanch.com/t/204527/java/Successful-Applet-Socket-Connection
- https://www.zhihu.com/question/20215561
- https://www.jianshu.com/p/42260a2575f8
- https://www.jianshu.com/p/59b5594ffbb0
- http://enterprisewebbook.com/ch8_websockets.html
- https://medium.com/@dominik.t/what-are-web-sockets-what-about-rest-apis-b9c15fd72aac
- https://medium.com/platform-engineer/web-api-design-35df8167460
- https://www.websocket.org/quantum.html
- https://blog.gtwang.org/web-development/websocket-protocol/
- https://medium.com/kifi-engineering/websockets-vs-regular-sockets-b3b8e7ea0708
- http://shouce.jb51.net/actionscript3.0/flash/net/Socket.html
- https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/XMLSocket.html
- https://github.com/theturtle32/AS3WebSocket
- https://baike.baidu.com/item/socket/281150
- https://baike.baidu.com/item/TCP%2FIP пакет протоколов
- https://www.cnblogs.com/xuehaoyue/p/6639029.html
- https://kotlintc.com/articles/4925
- https://blog.csdn.net/future_todo/article/details/50097363
- https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
- http://lamb-mei.com/462/websocket-%E7%A5%9E%E5%A5%87%E5%AD%97%E4%B8%B2-258eafa5-e914-47da-95ca-c5ab0dc85b11/
- https://kaazing.com/html5-websocket-security-is-strong/
- https://www.asni.cn/2152
(end) ----------------------------------------
Нажмите и удерживайте QR-код или найдите немного жизни, чтобы подписаться на нас