Skip to main content

Fast websocket client and server for asyncio

Project description

Introduction

GitHub Actions status for master branch Latest PyPI package version Downloads count Latest Read The Docs

picows is a library for building WebSocket clients and servers with a focus on performance.

picows is implemented in Cython and provides unparalleled performance compared to other popular WebSocket libraries.

https://raw.githubusercontent.com/tarasko/picows/master/docs/source/_static/picows_benchmark.png

The above chart shows the performance of echo clients communicating with a server through a loopback interface using popular Python libraries. boost.beast client is also included for reference. Typically, picows is ~1.5-2 times faster than aiohttp. All Python clients use uvloop. Please find the benchmark sources here.

Installation

picows requires Python 3.8 or greater and is available on PyPI. Use pip to install it:

$ pip install picows

Rationale

Popular WebSocket libraries attempt to provide high-level interfaces. They take care of timeouts, flow control, optional compression/decompression, assembling WebSocket messages from frames, as well as implementing async iteration interfaces. These features come with a significant cost even when messages are small, unfragmented (every WebSocket frame is final), and uncompressed. The async iteration interface is done using Futures, which adds extra work for the event loop and introduces delays. Furthermore, it is not always possible to check if more messages have already arrived; sometimes, only the last message matters.

API Design

The API follows the low-level transport/protocol design from asyncio. It passes frames instead of messages to a user handler. A message can potentially consist of multiple frames but it is up to user to choose the best strategy for merging them. Same principle applies for compression and flow control. User can implement their own strategies using the most appropriate tools.

That being said that the most common use-case is when messages and frames are the same, i.e. a message consists of only a single frame, and no compression is being used.

Getting started

Echo client

Connects to an echo server, sends a message and disconnect upon reply.

import asyncio
import uvloop
from picows import WSFrame, WSTransport, WSListener, ws_connect, WSMsgType

class ClientListener(WSListener):
    def on_ws_connected(self, transport: WSTransport):
        self.transport = transport
        transport.send(WSMsgType.TEXT, b"Hello world")

    def on_ws_frame(self, transport: WSTransport, frame: WSFrame):
        print(f"Echo reply: {frame.get_payload_as_ascii_text()}")
        transport.disconnect()


async def main(endpoint):
  (_, client) = await ws_connect(endpoint, ClientListener, "client")
  await client.transport.wait_until_closed()


if __name__ == '__main__':
  asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
  asyncio.run(main("ws://127.0.0.1:9001"))

This prints:

Echo reply: Hello world

Echo server

import asyncio
import uvloop
from picows import WSFrame, WSTransport, WSListener, ws_create_server, WSMsgType

class ServerClientListener(WSListener):
    def on_ws_connected(self, transport: WSTransport):
        print("New client connected")

    def on_ws_frame(self, transport: WSTransport, frame: WSFrame):
        transport.send(frame.msg_type, frame.get_payload_as_bytes())
        if frame.msg_type == WSMsgType.CLOSE:
            transport.disconnect()

async def main():
    url = "ws://127.0.0.1:9001"
    server = await ws_create_server(url, ServerClientListener, "server")
    print(f"Server started on {url}")
    await server.serve_forever()

if __name__ == '__main__':
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    asyncio.run(main())

Features

  • Maximally efficient WebSocket frame parser and builder implemented in Cython

  • Re-use memory as much as possible, avoid reallocations, and avoid unnecessary Python object creations

  • Provide Cython .pxd for efficient integration of user Cythonized code with picows

  • Ability to check if a frame is the last one in the receiving buffer

  • Support both secure and unsecure protocols (ws and wss schemes)

Documentation

https://picows.readthedocs.io/en/stable/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

picows-0.3.1.tar.gz (19.0 kB view details)

Uploaded Source

File details

Details for the file picows-0.3.1.tar.gz.

File metadata

  • Download URL: picows-0.3.1.tar.gz
  • Upload date:
  • Size: 19.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.0 CPython/3.12.5

File hashes

Hashes for picows-0.3.1.tar.gz
Algorithm Hash digest
SHA256 3c55891ee20642b9ae260474e310b76b7455f79d66eae7e2f61151d33c10ccce
MD5 2ae4225a1fb3fc3b1259e859d0e9236f
BLAKE2b-256 6efd717c39d8684b454b6c27b259fd0462e0e1a035e61b1b2643d15068922a63

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page