The websocket api (websockets)
Содержание:
- Common patterns¶
- Пример приложения
- Реализация сервера на PHP
- Отправка сообщений
- Комментарии
- Browser-based example¶
- Методы
- Специальные требования к серверу
- Common patterns¶
- License¶
- Client Example using the W3C WebSocket API
- License¶
- Метод второй: используйте nginx / apache для прокси-сервера wss
- Оповещение пользователя
- Server Example
- Методы
- Бизнес-кейс
- Обработка отключений клиента
- Basic example¶
- Итого
Common patterns¶
You will usually want to process several messages during the lifetime of a
connection. Therefore you must write a loop. Here are the basic patterns for
building a WebSocket server.
Consumer
For receiving messages and passing them to a coroutine:
async def consumer_handler(websocket, path): async for message in websocket await consumer(message)
In this example, represents your business logic for processing
messages received on the WebSocket connection.
Iteration terminates when the client disconnects.
Producer
For getting messages from a coroutine and sending them:
async def producer_handler(websocket, path): while True message = await producer() await websocket.send(message)
In this example, represents your business logic for generating
messages to send on the WebSocket connection.
raises a
exception when the client disconnects,
which breaks out of the loop.
Both
You can read and write messages on the same connection by combining the two
patterns shown above and running the two tasks in parallel:
async def handler(websocket, path): consumer_task = asyncio.ensure_future( consumer_handler(websocket, path)) producer_task = asyncio.ensure_future( producer_handler(websocket, path)) done, pending = await asyncio.wait( consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED, ) for task in pending task.cancel()
Пример приложения
Пример приложения в этой статье — это эхо-приложение. Оно имеет веб-страницу, которая устанавливает соединения WebSocket, а сервер перенаправляет все полученные сообщения обратно клиенту. Этот пример приложения не настроен для запуска из Visual Studio с IIS Express, поэтому его необходимо запустить в командной оболочке с и затем перейти в браузере по адресу . На этой веб-странице отображается состояние подключения.
Выберите Connect (Подключить), чтобы отправить запрос WebSocket на показанный URL-адрес. Введите тестовое сообщение и выберите Send (Отправить). После этого выберите Close Socket (Закрыть сокет). В разделе Communication Log (Журнал связи) выводится каждое выполняемое действие открытия, отправки и закрытия.
Реализация сервера на PHP
Исходники простого WebSocket echo-сервера выложил сюда.
Код хорошо документирован, но я всё же опишу некоторые тонкости реализации.
Чтобы «поднять» WebSocket сервер нужно создать обычный TCP-сервер.
В PHP TCP-сервер реализуется через «stream_socket» или через PHP расширение «sockets».
Различия между ними в том, что «stream_socket» работает на встроенных функциях PHP для работы с потоками, «sockets» же работает через модуль PHP и повторяет функции для работы с сокетами в языке «C».
Я выбрал «sockets».
Процесс реализован через «while» с задержкой 0.2 секунды.
Процесс не форкается и сообщения выбрасывает в консоль, поэтому запускать необходимо только через консоль.
Для того, чтобы обслуживать несколько клиентов одновременно, сокет делаю неблокирующим и через «socket_select»
каждые 0.2 секунды прослушиваю сокет.
При рукопожатии проверяю только наличие заголовков.
Фреймы парсю через «pack/unpack».
Сервер не понимает фрагментированных фреймов.
Сервер выдаёт только незамаскированные сообщения, т.к. некоторые браузеры не понимают замаскированных сообщений.
Сервер реагирует только на текстовые фреймы и фрейм закрытия соединения, бинарные фреймы не понимает.
Ну собственно всё, удачи в исследовании этого не простого протокола.
Отправка сообщений
Чтобы отправить сообщение по веб-сокет, нужно вызвать метод send() объекта WebSocket, передав ему данные для отправки.
socket.send(data);
Можно отправлять как текст, так и двоичные данные. В нашем приложении нужно передавать содержимое текстового поля на сервер при отправке формы. Чтобы сделать это, надо определить обработчик события отправки формы.
Добавьте следующий код в файл app.js.
// Отправка сообщения при отправке формы form.onsubmit = function(e) { e.preventDefault(); // Получение сообщения из текстового поля. var message = messageField.value; // Отправка сообщения по веб-сокету. socket.send(message); // Добавление сообщения в список сообщений. messagesList.innerHTML += '<liclass="sent"><span>Sent:</span>' + message + '</li>'; // Очистка текстового поля. messageField.value = ''; return false; };
При отправке формы приведенный выше код получит сообщение из messageField и отправит его через веб-сокет. Затем сообщение добавляется в messagesList и отображается на экране. После этого значение messageField очищается, чтобы пользователь мог ввести новое сообщение.
Комментарии
некоторые классы в System.Net.WebSockets пространстве имен поддерживаются в Windows 7, Windows Vista с пакетом обновления 2 (SP2) и Windows Server 2008. однако в Windows 8 и Windows Server 2012 поддерживаются только общедоступные реализации websocket клиента и сервера. классы и элементы класса в System.Net.WebSockets пространстве имен, которые поддерживаются в Windows 7, Windows Vista с пакетом обновления 2 (SP2) и Windows Server 2008, являются абстрактными классами. Это позволяет разработчику приложения наследовать и расширять эти абстрактные классы с помощью фактической реализации клиентских WebSocket.
Browser-based example¶
Here’s an example of how to run a WebSocket server and connect from a browser.
Run this script in a console:
#!/usr/bin/env python # WS server that sends messages at random intervals import asyncio import datetime import random import websockets async def time(websocket, path): while True now = datetime.datetime.utcnow().isoformat() + "Z" await websocket.send(now) await asyncio.sleep(random.random() * 3) start_server = websockets.serve(time, "127.0.0.1", 5678) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()
Then open this HTML file in a browser.
Методы
Отменяет соединение WebSocket и отменяет все ожидающие операции ввода-вывода. |
|
Закрывает подключение WebSocket в качестве асинхронной операции, используя подтверждение закрытия, которое определено в разделе 7 спецификации протокола WebSocket. |
|
Инициирует или завершает подтверждение закрытия, определенное в разделе 7 спецификации протокола WebSocket. |
|
Создайте буферы клиента для использования с этим экземпляром WebSocket. |
|
Этот API поддерживает инфраструктуру продукта и не предназначен для использования непосредственно из программного кода. Позволяет вызывающим объектам создать класс WebSocket на стороне клиента, который будет использовать WSPC для кадрирования. |
|
Создает новый WebSocket, работающий в указанном потоке, который представляет подключение к веб-сокету. |
|
Создает объект WebSocket , который работает с, Stream представляющим соединение через веб-сокет. |
|
Создает буфер сервера WebSocket. |
|
Используется для очистки неуправляемых ресурсов для ASP.NET и резидентных реализаций. |
|
Определяет, равен ли указанный объект текущему объекту. (Унаследовано от Object) |
|
Служит хэш-функцией по умолчанию. (Унаследовано от Object) |
|
Возвращает объект Type для текущего экземпляра. (Унаследовано от Object) |
|
Является устаревшей. Является устаревшей. Является устаревшей. возвращает значение, указывающее, предназначен ли экземпляр WebSocket платформа .NET Framework 4,5. |
|
Возвращает значение, указывающее, какое состояние экземпляра WebSocket — закрыто или прервано. |
|
Создает неполную копию текущего объекта Object. (Унаследовано от Object) |
|
Асинхронно получает данные через соединение WebSocket. |
|
Асинхронно получает данные через соединение WebSocket. |
|
Этот API поддерживает инфраструктуру продукта и не предназначен для использования непосредственно из программного кода. Является устаревшей. Разрешает вызывающим объектам регистрировать префиксы для запросов WebSocket (ws и wss). |
|
Асинхронно отправляет данные по соединению WebSocket. |
|
Асинхронно отправляет данные по соединению WebSocket. |
|
Асинхронно отправляет данные по соединению WebSocket. |
|
Проверяет, находится ли соединение в ожидаемом состоянии. |
|
Возвращает строку, представляющую текущий объект. (Унаследовано от Object) |
Специальные требования к серверу
В нашем случае лучше всего использовать сервер на основе цикла событий. Например, NodeJS, Kestrel или Twisted. Идея состоит в том, что при использовании потокового решения будет один поток на соединение. То есть, 1000 соединений = 1000 потоков. В решении на основе цикла событий у нас будет один поток для 1000 соединений.
- Вы можете принимать запросы EventSource только в том случае, если HTTP-запрос говорит, что он может принимать MIME-тип event-stream;
- Необходимо вести список всех подключенных пользователей, чтобы запускать новые события;
- Вы должны прослушивать сброшенные соединения и удалять их из списка подключенных пользователей;
- Вы должны поддерживать историю сообщений, чтобы при повторном подключении клиентов можно было отправить им пропущенные сообщения.
Мы получили все, чтобы приложение работало эффективно. Но столкнулись с некоторыми проблемами:
- Устаревшие прокси-серверы в некоторых случаях удаляют HTTP-соединения после короткого таймаута. Чтобы защитить соединения, авторы могут включать строку комментариев (начинающуюся с символа «:») каждые 15 секунд или около того.
- Авторы, желающие связать соединения источника событий друг с другом или с определенными ранее документами, могут обнаружить, что использование IP-адресов не работает. Отдельные клиенты могут иметь несколько IP-адресов (из-за наличия нескольких прокси-серверов) и отдельные IP-адреса могут иметь несколько клиентов (из-за совместного использования прокси-сервера). Лучше включать в документ уникальный идентификатор и передавать его как часть URL-адреса при установлении соединения.
- Использование chunked transfer encoding может уменьшить надежность HTTP протокола, если блокирование выполняется другим слоем, не подозревающим о требованиях к синхронизации. Если эта проблема возникнет, блокирование может быть отключено для обслуживания потоков событий.
- Клиенты, которые поддерживают ограничение на подключение к серверу через протокол HTTP, могут столкнуться с трудностями при открытии нескольких страниц сайта, если на каждой из этих страниц есть источник событий, расположенный в том же домене. Можно избежать этого, применяя механизм уникальных доменных имен для каждого соединения и разрешая пользователям включать функции EventSource для каждой страницы.
- Поддержка браузера и полифиллы: Microsoft Edge не поддерживает эту реализацию. Но существует полифиллы, которые позволяют решить данную проблему. Тем не менее, самый важный сегмент для SSE — это мобильные устройства, где браузеры IE / Edge распространены незначительно.
Некоторые из доступных полифиллов:
· Yaffle.
· amvtek.
· remy.
Бесплатное подключение и другие функции
Пользовательские агенты, работающие в контролируемых средах, могут отключить управление соединением с прокси-сервером в сети. В такой ситуации считается, что пользовательский агент включает как программное обеспечение мобильного устройства, так и сетевой прокси-сервер.
Например, браузер на мобильном устройстве, установив соединение, может обнаружить, что он находится в поддерживаемой сети, и попросить прокси-сервер сети взять на себя управление созданным соединением. Последовательность действий в такой ситуации может быть следующей:
- Браузер подключается к удаленному HTTP-серверу и запрашивает ресурс, указанный автором в конструкторе EventSource.
- Сервер отправляет случайные сообщения.
- В промежутке между двумя сообщениями браузер обнаруживает, что он неактивен, за исключением активности сети, связанной с поддержанием TCP- соединения, и решает переключиться в спящий режим для экономии энергии.
- Браузер отключается от сервера.
- Браузер связывается с сервисом в сети и просит, чтобы служба «push proxy» поддерживала соединение.
- Служба «push proxy» связывается с удаленным HTTP-сервером и запрашивает ресурс, указанный в конструкторе EventSource (возможно, включая HTTP-заголовок последнего события и т. д.).
- Браузер позволяет мобильному устройству перейти в спящий режим.
- Сервер отправляет другое сообщение.
- Служба «push proxy» использует технологию OMA push для передачи события на мобильное устройство, которое выходит из спящего режима на время, достаточное для обработки события. Затем возвращается в спящий режим.
Подобный подход может снизить объем передаваемых данных и привести к значительной экономии энергии.
Помимо реализации существующего API и формата передаваемых данных ext/event-stream также могут поддерживаться форматы фреймворка событий, определенные другими спецификациями.
Common patterns¶
You will usually want to process several messages during the lifetime of a
connection. Therefore you must write a loop. Here are the basic patterns for
building a WebSocket server.
Consumer
For receiving messages and passing them to a coroutine:
async def handler(websocket, path): while True: message = await websocket.recv() await consumer(message)
raises a
exception when the client
disconnects, which breaks out of the loop.
Producer
For getting messages from a coroutine and sending them:
async def handler(websocket, path): while True: message = await producer() await websocket.send(message)
raises a
exception when the client
disconnects, which breaks out of the loop.
Both
Of course, you can combine the two patterns shown above to read and write
messages on the same connection.
async def handler(websocket, path): while True: listener_task = asyncio.ensure_future(websocket.recv()) producer_task = asyncio.ensure_future(producer()) done, pending = await asyncio.wait( , return_when=asyncio.FIRST_COMPLETED) if listener_task in done: message = listener_task.result() await consumer(message) else: listener_task.cancel() if producer_task in done: message = producer_task.result() await websocket.send(message) else: producer_task.cancel()
(This code looks convoluted. If you know a more straightforward solution,
please let me know about it!)
License¶
Copyright (c) 2013-2014 Aymeric Augustin and contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of websockets nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Client Example using the W3C WebSocket API
var W3CWebSocket = require('websocket').w3cwebsocket; var client = new W3CWebSocket('ws://localhost:8080/', 'echo-protocol'); client.onerror = function() { console.log('Connection Error'); }; client.onopen = function() { console.log('WebSocket Client Connected'); function sendNumber() { if (client.readyState === client.OPEN) { var number = Math.round(Math.random() * 0xFFFFFF); client.send(number.toString()); setTimeout(sendNumber, 1000); } } sendNumber(); }; client.onclose = function() { console.log('echo-protocol Client Closed'); }; client.onmessage = function(e) { if (typeof e.data === 'string') { console.log("Received: '" + e.data + "'"); } };
License¶
Copyright (c) 2013-2015 Aymeric Augustin and contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of websockets nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Метод второй: используйте nginx / apache для прокси-сервера wss
В дополнение к использованию собственного SSL Workerman вы также можете использовать nginx / apache в качестве прокси-сервера wss для перенаправления на workerman (обратите внимание, что часть workerman этого метода не должна устанавливать ssl, иначе он не сможет подключиться). Принцип и процесс коммуникации:
Принцип и процесс коммуникации:
1. Клиент инициирует соединение wss для подключения к nginx / apache.
2. nginx / apache преобразует данные протокола wss в данные протокола ws и пересылает их на порт протокола WebSocket Workerman.
3. Workerman выполняет обработку бизнес-логики после получения данных.
4. Когда Workerman отправляет сообщение клиенту, происходит обратный процесс. Данные преобразуются в протокол wss с помощью nginx / apache и затем отправляются клиенту.
Оповещение пользователя
Если кликнуть по имени пользователя, то имя вставляется в поле ввода сообщения и заворачивается в фигурные скобки: «{» и «}».
На сервере имеется паттерн обработки такого текста (функция $worker->onMessage), который заменяет фигурные скобки на теги «<b>» и «</b>», выделяя текст жирным шрифтом.
Таким образом, когда пользователь получает сообщение, можно проверить наличие этих тегов и содержимого в них. Если в тегах содержится имя текущего пользователя, значит, в сообщении кто-то упомянул этого пользователя и надо его об этом уведомить. Это реализовано в методе обработки публичных сообщений:
void Widget::onPublicMessage(int userId, const QString &userName, const QString &userColor, const QString &text) { if (text.contains("" + m_userName + "")) { qApp->beep(); qApp->alert(this); } QString html = QString("%1 %3:" " %4") .arg(datetime()) .arg(userColor) .arg(userName) .arg(text) .arg(userId); ui->textBrowser->append(html); }
По такой же схеме можно реализовать полноценную поддержку markdown, вставку смайликов и картинок.
Расширяя функционал сервера и клиента можно также добавить:
- поддержку чат-комнат и полноценных приватных диалогов;
- сохранении истории сообщений в БД и её отправку при подключении или по запросу;
- статусы пользователей («Работаю», «Отдыхаю», «Отошёл» и др.);
- звуковые уведомления «Послать сигнал»;
- редактирование и удаление сообщений;
- цитирование сообщений других пользователей;
- передачу файлов.
Скриншот получившегося чата был в начале статьи, дополнительно приведу пример реального чата, реализованного по описанной в статье модели
Server Example
Here’s a short example showing a server that echos back anything sent to it, whether utf-8 or binary.
#!/usr/bin/env node var WebSocketServer = require('websocket').server; var http = require('http'); var server = http.createServer(function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }); server.listen(8080, function() { console.log((new Date()) + ' Server is listening on port 8080'); }); wsServer = new WebSocketServer({ httpServer: server, // You should not use autoAcceptConnections for production // applications, as it defeats all standard cross-origin protection // facilities built into the protocol and the browser. You should // *always* verify the connection's origin and decide whether or not // to accept it. autoAcceptConnections: false }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); return; } var connection = request.accept('echo-protocol', request.origin); console.log((new Date()) + ' Connection accepted.'); connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); connection.sendUTF(message.utf8Data); } else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); } }); connection.on('close', function(reasonCode, description) { console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); }); });
Методы
Закрывает WebSocket — подключение или заканчивает попытку подключения. Если подключение уже закрыто, этот метод не делает ничего.
void close( in optional unsigned short code, in optional DOMString reason );
Параметры
- Необязательный
- Числовое значение, обозначающее статус-код, описывающий почему подключение будет закрыто. Если параметр не указан, значение по умолчанию равно 1000(обозначает «обмен завершён»). Смотрите для , чтобы узнать разрешённые значения.
- Необязательный
- Читаемая человеком строка, объясняющая, почему подключение закрывается. Строка должна быть не длиннее, чем 123 байта UTF-8 текста (не символов).
Возможные исключения
- Указан неверный .
- Строка слишком длинные или содержит непарные суррогаты.
Примечания
В Gecko этот метод не поддерживает никакие параметры включительно до Gecko 8.0 (Firefox 8.0 / Thunderbird 8.0 / SeaMonkey 2.5).
Передаёт данные на сервер через WebSocket — соединение.
void send( in DOMString data ); void send( in ArrayBuffer data ); void send( in Blob data );
Параметры
- Текстовая строка, которая будет отправлена на сервер.
Возможные исключения
- Соединение ещё не открыто.
- Строка содержит непарные суррогаты
Заметьте: Gecko — реализация метода несколько отличается от специфицированной в Gecko 6.0; Gecko возвращает , обозначающий, открыто ли соединение до сих пор (и, в дополнение, были ли доставлены данные); это было исправлено в Gecko 8.0.
Начиная с Gecko 11.0, поддержка была реализована, но не типы данных.
Бизнес-кейс
Чтобы быстро добавлять новые виджеты в биржевое приложение и подключать их без перераспределения всей платформы, нужно, чтобы они были автономными и управляли своим собственным механизмом ввода-вывода данных.
Виджеты никак не связаны друг с другом. В идеале они все должны быть подписаны на какую-либо конечную точку API и начинать получать данные от нее.
Но при этом число подключений будет увеличиваться с ростом количества виджетов. Поэтому необходимо установить ограничение для браузеров по количеству одновременно обрабатываемых HTTP-запросов.
Данные, которые получат виджеты, в основном состоят из чисел и обновлений для этих чисел: первоначальный ответ содержит 10 акций со значениями их котировок.
Также данные должны включать в себя возможность добавления / удаления торгуемых акций, а также обновление текущих котировок. Мы передаем небольшое количество JSON-строк для каждого обновления так быстро, как это возможно.
HTTP / 2 обеспечивает мультиплексирование запросов, поступающих от одного домена. То есть, мы можем получить одно соединение для нескольких ответов.
Начнем с изучения различных вариантов получения данных и посмотрим, что может дать каждый из них.
- Мы собираемся использовать NGINX для балансировки нагрузки и реализации прокси, чтобы скрыть все конечные точки за одним и тем же доменом.
- Мы хотим эффективно использовать сетевой трафик и потребление заряда батареи пользовательского устройства.
Обработка отключений клиента
При отключении клиента из-за потери связи сервер не получает сведения автоматически. Сервер получает сообщение об отключении, только если клиент отправляет его. В случае потери подключения к Интернету это невозможно. Если при этом вы хотите выполнить определенное действие, установите время ожидания сигнала клиента с определенным интервалом.
Если клиент не всегда отправляет сообщения, а вы не хотите ожидать истечения времени ожидания только потому, что подключение не используется, тогда клиент может использовать таймер для отправки сообщения проверки связи каждые X секунд. Если сообщение не было получено на сервере в течение 2*X секунд после предыдущего, завершите подключение и сообщите о том, что клиент отключен. Подождите вдвое дольше ожидаемого временного интервала, чтобы оставить дополнительное время на задержки в сети при отправке сообщения проверки связи.
Basic example¶
This section assumes Python ≥ 3.5. For older versions, read below.
Here’s a WebSocket server example. It reads a name from the client, sends a
greeting, and closes the connection.
#!/usr/bin/env python import asyncio import websockets async def hello(websocket, path): name = await websocket.recv() print("< {}".format(name)) greeting = "Hello {}!".format(name) await websocket.send(greeting) print("> {}".format(greeting)) start_server = websockets.serve(hello, 'localhost', 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()
On the server side, the handler coroutine is executed once for each
WebSocket connection. The connection is automatically closed when the handler
returns.
Here’s a corresponding client example.
#!/usr/bin/env python import asyncio import websockets async def hello(): async with websockets.connect('ws://localhost:8765') as websocket: name = input("What's your name? ") await websocket.send(name) print("> {}".format(name)) greeting = await websocket.recv() print("< {}".format(greeting)) asyncio.get_event_loop().run_until_complete(hello())
and aren’t available in Python < 3.5. Here’s how to adapt
the client example for older Python versions.
Итого
WebSocket – современное средство коммуникации. Кросс-доменное, универсальное, безопасное.
На текущий момент он работает в браузерах IE10+, FF11+, Chrome 16+, Safari 6+, Opera 12.5+. В более старых версиях FF, Chrome, Safari, Opera есть поддержка черновых редакций протокола.
Там, где вебсокеты не работают – обычно используют другие транспорты, например . Вы найдёте их в других статьях этого раздела.
Есть и готовые библиотеки, реализующие функциональность COMET с использованием сразу нескольких транспортов, из которых вебсокет имеет приоритет. Как правило, библиотеки состоят из двух частей: клиентской и серверной.
Например, для Node.JS одной из самых известных библиотек является Socket.IO.
К недостаткам библиотек следует отнести то, что некоторые продвинутые возможности WebSocket, такие как двухсторонний обмен бинарными данными, в них недоступны. С другой – в большинстве случаев стандартного текстового обмена вполне достаточно.