что такое websocket python

WebSocket RPC или как написать живое WEB приложение для браузера

В статье речь пойдет о технологии WebSocket. Точнее не о самой технологии, а о том, как ее можно использовать. Я давно слежу за ней. Еще когда в 2011 году один мой коллега прислал мне ссылку на стандарт, пробежав глазами, я как-то расстроился. Выглядело настолько круто, и я думал, что в момент, когда это появится в популярных браузерах, я уже буду планировать, на что потратить свою пенсию. Но все оказалось не так, и как гласит caniuse.com WebSocket не поддерживается только в Opera Mini (надо бы провести голосование, как давно кто-либо видел Opera Mini).

Кто трогал WebSocketы руками, тот наверняка знает, что работать с API тяжело. В Javascript API достаточно низкоуровневый (принять сообщение — отправить сообщение), и придется разрабатывать алгоритм, как этими сообщениями обмениваться. Поэтому и была предпринята попытка упростить работу с вебсокетами.

Основная идея в том, чтобы дать разработчику простой API на Javascript вроде:

Особенности

Поясню некоторые моменты того, как это работает.

JavaScript

Браузер инициализирует новый объект RPC, после этого мы вызываем методы, но WebSocket еще не соединился. Не беда, вызовы стали в очередь, которую мы разгребаем при удачном соединении, или отвергаем все обещания (promises), очищая очередь при следующем неудачном соединении. Библиотека все время пытается соединиться с сервером (на события соединения и отсоединения тоже можно подписаться RPC.addEventListener(«onconnect», func)). Но пока мы не запустили RPC.connect(), мы мирно складываем вызовы в очередь внутри RPC.

После соединения сериализуем в JSON наши параметры и отправляем на сервер сообщение вида:

На что сервер отвечает:

где serial — это номер вызова.

После получения ответа библиотка на JS разрешает обещание (resolve promise), и мы вызываем то, что за then. После этого делаем еще один вызов и так далее…

Замечу также, что между вызовом и ответом на него, может пройти сколько угодно времени.

Python

На Python регистрируются вызовы в объекте WebSocket. Атрибут класса (class-property) ROUTES это словарь (dict), который хранит ассоциацию того, как называется вызов, и какая функция или класс его обслуживает.

Если указана функция, она просто вызывается, и ее результат передается клиенту.

Когда мы указываем класс, и клиент хоть раз вызывает его, мы создаем экземпляр этого класса и храним его вместе с соединением до самого его разрыва. Это очень удобно, можно сделать statefull соединение с браузером.

Доступ к методам осуществляется через точку. Если метод называется с подчеркивания (_hidden), то доступ из Javascript к нему не получить.

Еще от клиента к серверу, и от сервера к клиенту пробрасываются исключения. Когда я это реализовал, а был просто ошарашен. Увидеть Javascript traceback в питонячих логах — гарантированный когнтивный диссонанс. Ну, а про питонячьи Exceptions в JS я молчу.

Использую этот модуль на нескольких проектах. Везде работает как надо, основные баги вычистил.

Вместо заключения

Спасибо моим коллегам и друзъям за то, что помогали находить ошибки и иногда присылали патчи. Ну, и тебе, читатель. Если ты это читаешь, с учетом сухости статьи, тогда тебе уж точно интересна эта тема.

Источник

Как создавать веб-сокеты в Python

Jan 29, 2020 · 5 min read

WebSocket — протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени. — Википедия

Мы научимся настраивать собственный веб-сокет на Python, используя WebSockets API, который делает возможным двусторонний интерактивный сеанс связи между клиентом и сервером. С веб-сокетами вы можете отправлять и получать сообщения в режиме отслеживания событий без необходимости все время запрашивать данные у сервера, что уменьшает накладные расходы и позволяет передавать данные с сервера и на сервер в режиме реального времени.

Начинаем

Для работы с веб-сокетами требуется Python версии 3.6.1 и выше.

API в Python легко установить следующей командой:

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

Я не буду за т рагивать тему безопасности, и весь код будет на Python. Этого достаточно для понимания всеми, кто немного знаком с языком или с программированием в целом. Таким образом, в дальнейшем вам будет проще написать consumer, producer или даже сервер на другом языке или для фронтенд приложения.

Я надеюсь, что приведенные примеры будут вам полезны, и призываю разработчиков попробовать веб-сокеты хотя бы раз в своей карьере — они потрясающие. Это больше, чем REST, знаете ли!

Простой consumer сообщений

Итак, начнем с сопрограммы consumer, представленной выше. Я объясню каждую строку кода, чтобы вы смогли полностью понять, что происходит. Коротко: мы подключаемся к веб-сокету, указанному конкретным URL. Каждое сообщение, созданное сервером веб-сокета, логируется.

Три самые важные строки я объясню детально, но, если вас не интересует синтаксис, можете смело пропустить эту часть.

async/await — это просто специальный синтаксис для комфортной работы с промисами. Промис — не более чем объект, представляющий собой возможную передачу или сбой асинхронной операции.

Вместо передачи обратных вызовов в функцию вы можете присоединить обратные вызовы к этому возвращенному объекту.

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

Имейте в виду: иногда я использую аббревиатуру для веб-сокетов (ws), чтобы сделать код более читаемым в статье, но всегда пишу имя полностью в рабочем коде (Например, ws можно прочесть как веб-сайт или веб-сервер. Этого следует избегать хорошему разработчику. В конце концов код должен читаться как хорошая книга).

async for — это что-то вроде синхронного цикла for, позволяющего асинхронное восприятие.

Асинхронный IO позволяет выполнять перебор асинхронного итератора: вы можете вызывать асинхронный код на любом этапе перебора, в то время как обычный цикл for этого не позволяет. В этой строке кода веб-сокет — producer сообщений.

Для запуска этого простого consumer’а просто укажите имя хоста и порт, он запустится в постоянном режиме. Предельно просто. Не беспокойтесь, если нет цикла событий. asyncio создаст новый цикл и установит в качестве текущего.

Простой producer

Приведу пример producer’а, выдающего только одно значение. И он даже проще, чем comsumer:

Читайте также:  что делать когда только начались месячные

Код выше говорит сам за себя. Мы подключаемся к веб-сокету так же, как делали это ранее с consumer’ом. Получаем сообщение от сервера и ждем ответ. Когда мы получаем сообщение от сервера, узнаем, что сообщение было доставлено.

Теперь нам нужен только способ выполнить эту сопрограмму-отправитель только один раз.

Конечно, у Python есть решение. Мы можем просто использовать цикл обработки событий так же, как мы делали это с consumer. Единственное отличие будет в том, что он будет запущен, пока мы не получим ответ от сервера. После получения ответа задача завершится.

В Python 3.7 стало еще лучше — теперь можно использовать функцию run для выполнения сопрограмм.

Сервер: последний кусочек пазла

Для этого случая я написал серверный класс, который объединяет весь функционал сервера. Этот сервер рассылает сообщения, отправленные producer’ом, всем слушающим consumer’ам.

Сервер создается и определяет сопрограмму обработчика веб-сокета. Функция веб-сокета serve — это обёртка вокруг метода create_server() цикла обработки событий. Он создает и запускает сервер с create_server() и принимает обработчик веб-сокета в качестве аргумента.

Последняя часть кода — самая длинная, оставайтесь с нами, мы почти закончили.

Источник

Getting startedВ¶

RequirementsВ¶

websockets requires Python ≥ 3.6.1.

You should use the latest version of Python if possible. If you’re using an older version, be aware that for each minor version (3.x), only the latest bugfix release (3.x.y) is officially supported.

InstallationВ¶

Install websockets with:

Basic exampleВ¶

Here’s a WebSocket server example.

It reads a name from the client, sends a greeting, and closes the connection.

On the server side, websockets executes the handler coroutine hello once for each WebSocket connection. It closes the connection when the handler coroutine returns.

Here’s a corresponding WebSocket client example.

Using connect() as an asynchronous context manager ensures the connection is closed before exiting the hello coroutine.

Secure exampleВ¶

Secure WebSocket connections improve confidentiality and also reliability because they reduce the risk of interference by bad proxies.

The WSS protocol is to WS what HTTPS is to HTTP: the connection is encrypted with Transport Layer Security (TLS) — which is often referred to as Secure Sockets Layer (SSL). WSS requires TLS certificates like HTTPS.

Here’s how to adapt the server example to provide secure connections. See the documentation of the ssl module for configuring the context securely.

Here’s how to adapt the client.

This client needs a context because the server uses a self-signed certificate.

A client connecting to a secure WebSocket server with a valid certificate (i.e. signed by a CA that your Python installation trusts) can simply pass ssl=True to connect() instead of building a context.

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:

Then open this HTML file in a browser.

Synchronization exampleВ¶

A WebSocket server can receive events from clients, process them to update the application state, and synchronize the resulting state across clients.

Here’s an example where any client can increment or decrement a counter. Updates are propagated to all connected clients.

The concurrency model of asyncio guarantees that updates are serialized.

Run this script in a console:

Then open this HTML file in several browsers.

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 consumer coroutine:

In this example, consumer represents your business logic for processing messages received on the WebSocket connection.

Iteration terminates when the client disconnects.

ProducerВ¶

For getting messages from a producer coroutine and sending them:

In this example, producer represents your business logic for generating messages to send on the WebSocket connection.

send() raises a ConnectionClosed exception when the client disconnects, which breaks out of the while True 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:

RegistrationВ¶

As shown in the synchronization example above, if you need to maintain a list of currently connected clients, you must register them when they connect and unregister them when they disconnect.

This simplistic example keeps track of connected clients in memory. This only works as long as you run a single process. In a practical application, the handler may subscribe to some channels on a message broker, for example.

That’s all!¶

The design of the websockets API was driven by simplicity.

You don’t have to worry about performing the opening or the closing handshake, answering pings, or any other behavior required by the specification.

websockets handles all this under the hood so you don’t have to.

Источник

Getting started¶

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.

On the server side, the handler coroutine hello is executed once for each WebSocket connection. The connection is automatically closed when the handler returns.

Here’s a corresponding client example.

async and await aren’t available in Python consumer coroutine:

recv() raises a ConnectionClosed exception when the client disconnects, which breaks out of the while True loop.

Producer¶

For getting messages from a producer coroutine and sending them:

send() raises a ConnectionClosed exception when the client disconnects, which breaks out of the while True loop.

Of course, you can combine the two patterns shown above to read and write messages on the same connection.

(This code looks convoluted. If you know a more straightforward solution, please let me know about it!)

Registration¶

If you need to maintain a list of currently connected clients, you must register clients when they connect and unregister them when they disconnect.

This simplistic example keeps track of connected clients in memory. This only works as long as you run a single process. In a practical application, the handler may subscribe to some channels on a message broker, for example.

Читайте также:  что делать когда хочется есть а еды нету

That’s all!¶

The design of the websockets API was driven by simplicity.

You don’t have to worry about performing the opening or the closing handshake, answering pings, or any other behavior required by the specification.

websockets handles all this under the hood so you don’t have to.

Python await and async syntax introduced in Python 3.5.

If you’re using Python 3.4 or 3.3, you must substitute:

Источник

WebSockets¶

websockets is a library for developing WebSocket servers and clients in Python. It implements RFC 6455 with a focus on correctness and simplicity. It passes the Autobahn Testsuite.

Built on top of Python’s asynchronous I/O support introduced in PEP 3156, it provides an API based on coroutines, making it easy to write highly concurrent applications.

Bug reports, patches and suggestions welcome! Just open an issue or send a pull request.

Example¶

Here’s a WebSocket server example. It reads a name from the client and sends a message.

Here’s a corresponding client example.

On the server side, the handler coroutine hello is executed once for each WebSocket connection. The connection is automatically closed when the handler returns.

You will almost always want to process several messages during the lifetime of a connection. Therefore you must write a loop. Here are the recommended patterns to exit cleanly when the connection drops, either because the other side closed it or for any other reason.

For receiving messages and passing them to a consumer coroutine:

recv() returns None when the connection is closed. In other words, None marks the end of the message stream. The handler coroutine should check for that case and return when it happens.

For getting messages from a producer coroutine and sending them:

send() fails with an exception when it’s called on a closed connection. Therefore the handler coroutine should check that the connection is still open before attempting to write and return otherwise.

Of course, you can combine the two patterns shown above to read and write messages on the same connection:

(This code looks convoluted. If you know a more straightforward solution, please let me know about it!)

That’s really all you have to know! websockets manages the connection under the hood so you don’t have to.

Cheat sheet¶

Server¶

Client¶

Debugging¶

If you don’t understand what websockets is doing, enable logging:

Design¶

websockets provides complete client and server implementations, as shown in the examples above. These functions are built on top of low-level APIs reflecting the two phases of the WebSocket protocol:

The first phase is designed to integrate with existing HTTP software. websockets provides functions to build and validate the request and response headers.

The second phase is the core of the WebSocket protocol. websockets provides a standalone implementation on top of asyncio with a very simple API.

For convenience, public APIs can be imported directly from the websockets package, unless noted otherwise. Anything that isn’t listed in this document is a private API.

High-level API¶

Server¶

The websockets.server module defines a simple WebSocket server API.

websockets.server. serve ( ws_handler, host=None, port=None, *, loop=None, klass=WebSocketServerProtocol, origins=None, subprotocols=None, extra_headers=None, **kwds ) [source] ¶

This coroutine creates a WebSocket server.

ws_handler is the WebSocket handler. It must be a coroutine accepting two arguments: a WebSocketServerProtocol and the request URI.

serve() accepts several optional arguments:

serve() yields a Server which provides:

Since there’s no useful way to propagate exceptions triggered in handlers, they’re sent to the ‘websockets.server’ logger instead. Debugging is much easier if you configure logging to print them:

For the sake of simplicity, it doesn’t rely on a full HTTP implementation. Its support for HTTP responses is very limited.

handshake ( origins=None, subprotocols=None, extra_headers=None ) [source] ¶

Perform the server side of the opening handshake.

If provided, origins is a list of acceptable HTTP Origin values. Include » if the lack of an origin is acceptable.

If provided, subprotocols is a list of supported subprotocols in order of decreasing preference.

If provided, extra_headers sets additional HTTP response headers. It can be a mapping or an iterable of (name, value) pairs. It can also be a callable taking the request path and headers in arguments.

Return the URI of the request.

select_subprotocol ( client_protos, server_protos ) [source] ¶

Pick a subprotocol among those offered by the client.

Client¶

The websockets.client module defines a simple WebSocket client API.

websockets.client. connect ( uri, *, loop=None, klass=WebSocketClientProtocol, origin=None, subprotocols=None, extra_headers=None, **kwds ) [source] ¶

This coroutine connects to a WebSocket server.

connect() accepts several optional arguments:

origin sets the Origin HTTP header

extra_headers sets additional HTTP request headers – it can be a mapping or an iterable of (name, value) pairs

connect() yields a WebSocketClientProtocol which can then be used to send and receive messages.

It raises InvalidURI if uri is invalid and InvalidHandshake if the handshake fails.

class websockets.client. WebSocketClientProtocol ( *, host=None, port=None, secure=None, timeout=10, max_size=2 ** 20, loop=None ) [source] ¶

handshake ( wsuri, origin=None, subprotocols=None, extra_headers=None ) [source] ¶

Perform the client side of the opening handshake.

If provided, origin sets the Origin HTTP header.

If provided, subprotocols is a list of supported subprotocols in order of decreasing preference.

If provided, extra_headers sets additional HTTP request headers. It must be a mapping or an iterable of (name, value) pairs.

Shared¶

The websockets.protocol module handles WebSocket control and data frames as specified in sections 4 to 8 of RFC 6455.

class websockets.protocol. WebSocketCommonProtocol ( *, host=None, port=None, secure=None, timeout=10, max_size=2 ** 20, loop=None ) [source] ¶

This class implements common parts of the WebSocket protocol.

It runs a task that stores incoming data frames in a queue and deals with control frames automatically. It sends outgoing data frames and performs the closing handshake.

The timeout parameter defines the maximum wait time in seconds for completing the closing handshake and, only on the client side, for terminating the TCP connection. close() will complete in at most this time on the server side and twice this time on the client side.

Читайте также:  что делать если тюль оказалась длинной а внизу утяжелитель

The max_size parameter enforces the maximum size for incoming messages in bytes. The default value is 1MB. None disables the limit. If a message larger than the maximum size is received, recv() will return None and the connection will be closed with status code 1009.

Once the handshake is complete, request and response HTTP headers are available:

If a subprotocol was negotiated, it’s available in the subprotocol attribute.

Local address of the connection.

The address is a (host, port) tuple or None if the connection hasn’t been established yet.

Remote address of the connection.

The address is a (host, port) tuple or None if the connection hasn’t been established yet.

This property is True when the connection is usable.

It may be used to handle disconnections gracefully.

This coroutine performs the closing handshake.

It waits for the other end to complete the handshake. It doesn’t do anything once the connection is closed.

It’s usually safe to wrap this coroutine in async() since errors during connection termination aren’t particularly useful.

This coroutine receives the next message.

It returns a str for a text frame and bytes for a binary frame.

This coroutine sends a message.

It sends a str as a text frame and bytes as a binary frame.

It raises a TypeError for other inputs and InvalidState once the connection is closed.

This coroutine sends a ping.

It returns a Future which will be completed when the corresponding pong is received and which you may ignore if you don’t want to wait.

A ping may serve as a keepalive.

This coroutine sends a pong.

An unsolicited pong may serve as a unidirectional heartbeat.

Exceptions¶

Exception raised when a handshake request or response is invalid.

exception websockets.exceptions. InvalidOrigin [source] ¶

Exception raised when the origin in a handshake request is forbidden.

exception websockets.exceptions. InvalidState [source] ¶

Exception raised when an operation is forbidden in the current state.

exception websockets.exceptions. InvalidURI [source] ¶

Exception raised when an URI isn’t a valid websocket URI.

exception websockets.exceptions. PayloadTooBig [source] ¶

Exception raised when a frame’s payload exceeds the maximum size.

exception websockets.exceptions. WebSocketProtocolError [source] ¶

Internal exception raised when the remote side breaks the protocol.

Low-level API¶

Opening handshake¶

The websockets.handshake module deals with the WebSocket opening handshake according to section 4 of RFC 6455.

It provides functions to implement the handshake with any existing HTTP library. You must pass to these functions:

The inputs and outputs of get_header and set_header are str objects containing only ASCII characters.

Some checks cannot be performed because they depend too much on the context; instead, they’re documented below.

To accept a connection, a server must:

To open a connection, a client must:

Build a handshake request to send to the server.

websockets.handshake. check_request ( get_header ) [source] ¶

Check a handshake request received from the client.

Otherwise it raises an InvalidHandshake exception and the server must return an error like 400 Bad Request.

This function doesn’t verify that the request is an HTTP/1.1 or higher GET request and doesn’t perform Host and Origin checks. These controls are usually performed earlier in the HTTP request handling code. They’re the responsibility of the caller.

websockets.handshake. build_response ( set_header, key ) [source] ¶

Build a handshake response to send to the client.

websockets.handshake. check_response ( get_header, key ) [source] ¶

Check a handshake response received from the server.

Otherwise it raises an InvalidHandshake exception.

This function doesn’t verify that the response is an HTTP/1.1 or higher response with a 101 status code. These controls are the responsibility of the caller.

Data transfer¶

The websockets.framing module implements data framing as specified in section 5 of RFC 6455.

class websockets.framing. Frame ¶

Alias for field number 2

Alias for field number 0

Alias for field number 1

websockets.framing. read_frame ( reader, mask, *, max_size=None ) [source] ¶

Read a WebSocket frame and return a Frame object.

reader is a coroutine taking an integer argument and reading exactly this number of bytes, unless the end of file is reached.

mask is a bool telling whether the frame should be masked i.e. whether the read happens on the server side.

If max_size is set and the payload exceeds this size in bytes, PayloadTooBig is raised.

This function validates the frame before returning it and raises WebSocketProtocolError if it contains incorrect values.

websockets.framing. write_frame ( frame, writer, mask ) [source] ¶

Write a WebSocket frame.

frame is the Frame object to write.

writer is a function accepting bytes.

mask is a bool telling whether the frame should be masked i.e. whether the write happens on the client side.

This function validates the frame before sending it and raises WebSocketProtocolError if it contains incorrect values.

websockets.framing. parse_close ( data ) [source] ¶

Parse the data in a close frame.

websockets.framing. serialize_close ( code, reason ) [source] ¶

Serialize the data for a close frame.

URI parser¶

The websockets.uri module implements parsing of WebSocket URIs according to section 3 of RFC 6455.

websockets.uri. parse_uri ( uri ) [source] ¶

This function parses and validates a WebSocket URI.

Otherwise it raises an InvalidURI exception.

class websockets.uri. WebSocketURI ¶

Alias for field number 1

Alias for field number 2

Alias for field number 3

Alias for field number 0

Utilities¶

The websockets.http module provides HTTP parsing functions. They’re merely adequate for the WebSocket handshake messages.

websockets.http. read_request ( stream ) [source] ¶

Raise an exception if the request isn’t well formatted.

The request is assumed not to contain a body.

websockets.http. read_response ( stream ) [source] ¶

Raise an exception if the request isn’t well formatted.

The response is assumed not to contain a body.

Changelog¶

Limitations¶

Extensions aren’t implemented. No extensions are registered at the time of writing.

The client doesn’t attempt to guarantee that there is no more than one connection to a given IP adress in a CONNECTING state.

The client doesn’t support connecting through a proxy.

Источник

Строительный портал