Python websockets kết nối cuộc gọi không thành công

websockets is a library for building WebSocket servers and clients in Python with a focus on correctness, simplicity, robustness, and performance

Được xây dựng dựa trên asyncio, khung I/O không đồng bộ tiêu chuẩn của Python, nó cung cấp một API dựa trên coroutine tao nhã

Here's how a client sends and receives messages

#!/usr/bin/env python
import asyncio
import websockets
async def hello[]:

async with websockets.connect["ws://localhost:8765"] as websocket:
await websocket.send["Hello world!"]
await websocket.recv[] asyncio.run[hello[]]

Và đây là một máy chủ tiếng vang

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]

Don't worry about the opening and closing handshakes, pings and pongs, or any other behavior described in the specification. websockets takes care of this under the hood so you can focus on your application

Also, websockets provides an interactive client

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].

Do you like it? Let's dive in

GETTING STARTED

Requirements

websockets requires Python ≥ 3. 7

Use the most recent Python release

Đối với mỗi phiên bản phụ [3. x], only the latest bugfix or security release [3. x. y] is officially supported

It doesn't have any dependencies

Cài đặt

Install websockets with

$ pip install websockets

Wheels are available for all platforms

Tutorial

Learn how to build an real-time web application with websockets

Phần 1 - Gửi và nhận

In this tutorial, you're going to build a web-based Connect Four game

The web removes the constraint of being in the same room for playing a game. Two players can connect over of the Internet, regardless of where they are, and play in their browsers

When a player makes a move, it should be reflected immediately on both sides. This is difficult to implement over HTTP due to the request-response style of the protocol

Indeed, there is no good way to be notified when the other player makes a move. Workarounds such as polling or long-polling introduce significant overhead

Nhập WebSocket

The WebSocket protocol provides two-way communication between a browser and a server over a persistent connection. That's exactly what you need to exchange moves between players, via a server

Đây là phần đầu tiên của hướng dẫn

  • In this first part, you will create a server and connect one browser; you can play if you share the same browser
  • Trong phần thứ hai, bạn sẽ kết nối trình duyệt thứ hai;
  • In the third part, you will deploy the game to the web; you can play from any browser connected to the Internet

Prerequisites

This tutorial assumes basic knowledge of Python and JavaScript

Nếu bạn cảm thấy thoải mái với môi trường ảo, bạn có thể sử dụng một môi trường cho hướng dẫn này. Else, don't worry. websockets doesn't have any dependencies; it shouldn't create trouble in the default environment

If you haven't installed websockets yet, do it now

$ pip install websockets

Confirm that websockets is installed

$ python -m websockets --version

This tutorial is written for websockets 10. 4

If you installed another version, you should switch to the corresponding version of the documentation

Download the starter kit

Tạo một thư mục và tải xuống ba tệp này. connect4. js, connect4. css, and connect4. py

The JavaScript module, along with the CSS file, provides a web-based user interface. Here's its API

connect4. PLAYER1Color of the first player

connect4. PLAYER2Color of the second player

connect4. createBoard[board]Draw a board

Arguments

•board -- DOM element containing the board; must be initially empty

connect4. playMove[board, player, column, row]Play a move

Arguments

  • board -- DOM element containing the board
  • player -- PLAYER1 or PLAYER2
  • column -- between 0 and 6
  • row -- between 0 and 5

The Python module provides a class to record moves and tell when a player wins. Here's its API

connect4. PLAYER1 = "red"Color of the first player

connect4. PLAYER2 = "yellow"Color of the second player

class connect4. Connect4A Connect Four game

play[player, column]Play a move

Parameters

  • player -- PLAYER1 or PLAYER2
  • column -- between 0 and 6

ReturnsRow where the checker lands, between 0 and 5. RaisesRuntimeError -- if the move is illegal

movesList of moves played during this game, as [player, column, row] tuples

winnerPLAYER1 or PLAYER2 if they won; None if the game is still ongoing

Bootstrap the web UI

Create an index. html file next to connect4. js and connect4. css with this content




Connect Four




This HTML page contains an empty

element where you will draw the Connect Four board. It loads a main.js script where you will write all your JavaScript code.

Tạo một chính. js file next to index. html. In this script, when the page loads, draw the board

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];

Open a shell, navigate to the directory containing these files, and start a HTTP server

$ python -m http.server

Open http. //localhost. 8000/ in a web browser. The page displays an empty board with seven columns and six rows. You will play moves in this board later

Khởi động máy chủ

Create an app. py file next to connect4. py with this content

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]

The entry point of this program is asyncio. run[main[]]. It creates an asyncio event loop, runs the main[] coroutine, and shuts down the loop

The main[] coroutine calls serve[] to start a websockets server. serve[] takes three positional arguments

  • handler is a coroutine that manages a connection. When a client connects, websockets calls handler with the connection in argument. When handler terminates, websockets closes the connection
  • The second argument defines the network interfaces where the server can be reached. Here, the server listens on all interfaces, so that other devices on the same local network can connect
  • The third argument is the port on which the server listens

Invoking serve[] as an asynchronous context manager, in an async with block, ensures that the server shuts down properly when terminating the program

For each connection, the handler[] coroutine runs an infinite loop that receives messages from the browser and prints them

Open a shell, navigate to the directory containing app. py, and start the server

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
0

This doesn't display anything. Hopefully the WebSocket server is running. Let's make sure that it works. You cannot test the WebSocket server with a web browser like you tested the HTTP server. However, you can test it with websockets' interactive client

Open another shell and run this command

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
1

You get a prompt. Type a message and press "Enter". Switch to the shell where the server is running and check that the server received the message. Good

Exit the interactive client with Ctrl-C or Ctrl-D

Now, if you look at the console where you started the server, you can see the stack trace of an exception

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
2

Indeed, the server was waiting for the next message with recv[] when the client disconnected. When this happens, websockets raises a ConnectionClosedOK exception to let you know that you won't receive another message on this connection

This exception creates noise in the server logs, making it more difficult to spot real errors when you add functionality to the server. Catch it in the handler[] coroutine

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
3

Stop the server with Ctrl-C and start it again

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
0

You must restart the WebSocket server when you make changes

The WebSocket server loads the Python code in app. py then serves every WebSocket request with this version of the code. As a consequence, changes to app. py aren't visible until you restart the server

This is unlike the HTTP server that you started earlier with python -m http. server. For every request, this HTTP server reads the target file and sends it. That's why changes are immediately visible

It is possible to restart the WebSocket server automatically but this isn't necessary for this tutorial

Hãy thử kết nối và ngắt kết nối lại máy khách tương tác. The ConnectionClosedOK exception doesn't appear anymore

This pattern is so common that websockets provides a shortcut for iterating over messages received on the connection until the client disconnects

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
5

Restart the server and check with the interactive client that its behavior didn't change

At this point, you bootstrapped a web application and a WebSocket server. Hãy kết nối chúng

Truyền từ trình duyệt đến máy chủ

In JavaScript, you open a WebSocket connection as follows

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
6

Trước khi bạn trao đổi tin nhắn với máy chủ, bạn cần quyết định định dạng của chúng. Không có quy ước chung cho việc này

Hãy sử dụng các đối tượng JSON với khóa loại xác định loại sự kiện và phần còn lại của đối tượng chứa các thuộc tính của sự kiện

Đây là một sự kiện mô tả một nước đi ở ô giữa của bàn cờ

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
7

Đây là cách tuần tự hóa sự kiện này thành JSON và gửi nó đến máy chủ

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
8

Now you have all the building blocks to send moves to the server

Thêm chức năng này vào chính. js

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
9

sendMoves[] registers a listener for click events on the board. The listener figures out which column was clicked, builds a event of type "play", serializes it, and sends it to the server

Modify the initialization to open the WebSocket connection and call the sendMoves[] function

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
0

Check that the HTTP server and the WebSocket server are still running. If you stopped them, here are the commands to start them again

$ python -m http.server

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
0

Refresh http. //localhost. 8000/ in your web browser. Click various columns in the board. The server receives messages with the expected column number

There isn't any feedback in the board because you haven't implemented that yet. Let's do it

Transmit from server to browser

In JavaScript, you receive WebSocket messages by listening to message events. Here's how to receive a message from the server and deserialize it from JSON

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
3

You're going to need three types of messages from the server to the browser

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
4

The JavaScript code receiving these messages will dispatch events depending on their type and take appropriate action. For example, it will react to an event of type "play" by displaying the move on the board with the playMove[] function

Thêm chức năng này vào chính. js

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
5

Why does showMessage use window. setTimeout?

When playMove[] modifies the state of the board, the browser renders changes asynchronously. Conversely, window. alert[] runs synchronously and blocks rendering while the alert is visible

If you called window. alert[] immediately after playMove[], the browser could display the alert before rendering the move. You could get a "Player red wins. " cảnh báo mà không nhìn thấy nước đi cuối cùng của màu đỏ

We're using window. alert[] for simplicity in this tutorial. A real application would display these messages in the user interface instead. It wouldn't be vulnerable to this problem

Modify the initialization to call the receiveMoves[] function

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
6

At this point, the user interface should receive events properly. Let's test it by modifying the server to send some events

Sending an event from Python is quite similar to JavaScript

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
7

Don't forget to serialize the event with json. dumps[]

Else, websockets raises TypeError. data is a dict-like object

Modify the handler[] coroutine in app. py as follows

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
8

Restart the WebSocket server and refresh http. //localhost. 8000/ in your web browser. Seven moves appear at 0. 5 second intervals. Then an alert announces the winner

Good. Now you know how to communicate both ways

Once you plug the game engine to process moves, you will have a fully functional game

Add the game logic

In the handler[] coroutine, you're going to initialize a game

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
9

Then, you're going to iterate over incoming messages and take these steps

  • parse an event of type "play", the only type of event that the user interface sends;
  • play the move in the board with the play[] method, alternating between the two players;
  • if play[] raises RuntimeError because the move is illegal, send an event of type "error";
  • else, send an event of type "play" to tell the user interface where the checker lands;
  • if the move won the game, send an event of type "win"

Try to implement this by yourself

Keep in mind that you must restart the WebSocket server and reload the page in the browser when you make changes

When it works, you can play the game from a single browser, with players taking alternate turns

Enable debug logs to see all messages sent and received

Here's how to enable debug logs

$ pip install websockets
0

If you're stuck, a solution is available at the bottom of this document

Summary

In this first part of the tutorial, you learned how to

  • build and run a WebSocket server in Python with serve[];
  • receive a message in a connection handler with recv[];
  • send a message in a connection handler with send[];
  • iterate over incoming messages with async for message in websocket. . ;
  • open a WebSocket connection in JavaScript with the WebSocket API;
  • send messages in a browser with WebSocket. send[];
  • receive messages in a browser by listening to message events;
  • design a set of events to be exchanged between the browser and the server

Giờ đây, bạn có thể chơi trò chơi Connect Four trong trình duyệt, giao tiếp qua kết nối WebSocket với máy chủ chứa logic trò chơi

However, the two players share a browser, so the constraint of being in the same room still applies

Move on to the second part of the tutorial to break this constraint and play from separate browsers

Solution

app. py

$ pip install websockets
1

index. html




Connect Four




main. js

$ pip install websockets
3

Part 2 - Route & broadcast

This is the second part of the tutorial

  • In the first part, you created a server and connected one browser; you could play if you shared the same browser
  • In this second part, you will connect a second browser; you can play from different browsers on a local network
  • In the third part, you will deploy the game to the web; you can play from any browser connected to the Internet

In the first part of the tutorial, you opened a WebSocket connection from a browser to a server and exchanged events to play moves. The state of the game was stored in an instance of the Connect4 class, referenced as a local variable in the connection handler coroutine

Now you want to open two WebSocket connections from two separate browsers, one for each player, to the same server in order to play the same game. This requires moving the state of the game to a place where both connections can access it

Share game state

As long as you're running a single server process, you can share state by storing it in a global variable

What if you need to scale to multiple server processes?

In that case, you must design a way for the process that handles a given connection to be aware of relevant events for that client. This is often achieved with a publish / subscribe mechanism

How can you make two connection handlers agree on which game they're playing? When the first player starts a game, you give it an identifier. Then, you communicate the identifier to the second player. When the second player joins the game, you look it up with the identifier

In addition to the game itself, you need to keep track of the WebSocket connections of the two players. Since both players receive the same events, you don't need to treat the two connections differently; you can store both in the same set

Let's sketch this in code

A module-level dict enables lookups by identifier

$ pip install websockets
4

When the first player starts the game, initialize and store it

$ pip install websockets
5

When the second player joins the game, look it up

$ pip install websockets
6

Notice how we're carefully cleaning up global state with try. . finally. . blocks. Else, we could leave references to games or connections in global state, which would cause a memory leak

In both connection handlers, you have a game pointing to the same Connect4 instance, so you can interact with the game, and a connected set of connections, so you can send game events to both players as follows

$ pip install websockets
7

Perhaps you spotted a major piece missing from the puzzle. How does the second player obtain join_key? Let's design new events to carry this information

To start a game, the first player sends an "init" event

$ pip install websockets
8

The connection handler for the first player creates a game as shown above and responds with

$ pip install websockets
9

With this information, the user interface of the first player can create a link to //localhost:8000/?join=. For the sake of simplicity, we will assume that the first player shares this link with the second player outside of the application, for example via an instant messaging service.

To join the game, the second player sends a different "init" event

$ pip install websockets
9

The connection handler for the second player can look up the game with the join key as shown above. There is no need to respond

Let's dive into the details of implementing this design

Start a game

We'll start with the initialization sequence for the first player

In main. js, define a function to send an initialization event when the WebSocket connection is established, which triggers an open event

$ pip install websockets
1

Update the initialization sequence to call initGame[]

$ pip install websockets
2

In app. py, define a new handler coroutine — keep a copy of the previous one to reuse it later

$ pip install websockets
3

In index.html, add an element to display the link to share with the other player.

$ pip install websockets
4

In main. js, modify receiveMoves[] to handle the "init" message and set the target of that link

$ pip install websockets
5

Restart the WebSocket server and reload http. //localhost. 8000/ in the browser. There's a link labeled JOIN below the board with a target that looks like http. //localhost. 8000/?join=95ftAaU5DJVP1zvb

The server logs say first player started game . If you click the board, you see "play" events. There is no feedback in the UI, though, because you haven't restored the game logic yet

Before we get there, let's handle links with a join query parameter

Join a game

We'll now update the initialization sequence to account for the second player

In main. js, update initGame[] to send the join key in the "init" message when it's in the URL

$ pip install websockets
6

In app. py, update the handler coroutine to look for the join key in the "init" message, then load that game

$ pip install websockets
7

Restart the WebSocket server and reload http. //localhost. 8000/ in the browser

Copy the link labeled JOIN and open it in another browser. You may also open it in another tab or another window of the same browser; however, that makes it a bit tricky to remember which one is the first or second player

You must start a new game when you restart the server

Since games are stored in the memory of the Python process, they're lost when you stop the server

Whenever you make changes to app. py, you must restart the server, create a new game in a browser, and join it in another browser

The server logs say first player started game . and second player joined game . The numbers match, proving that the game local variable in both connection handlers points to same object in the memory of the Python process

Click the board in either browser. The server receives "play" events from the corresponding player

In the initialization sequence, you're routing connections to start[] or join[] depending on the first message received by the server. This is a common pattern in servers that handle different clients

Why not use different URIs for start[] and join[]?

Instead of sending an initialization event, you could encode the join key in the WebSocket URI e.g. ws://localhost:8001/join/. The WebSocket server would parse websocket.path and route the connection, similar to how HTTP servers route requests.

When you need to send sensitive data like authentication credentials to the server, sending it an event is considered more secure than encoding it in the URI because URIs end up in logs

For the purposes of this tutorial, both approaches are equivalent because the join key comes from a HTTP URL. There isn't much at risk anyway

Now you can restore the logic for playing moves and you'll have a fully functional two-player game

Add the game logic

Once the initialization is done, the game is symmetrical, so you can write a single coroutine to process the moves of both players

$ pip install websockets
8

With such a coroutine, you can replace the temporary code for testing in start[] by

$ pip install websockets
9

and in join[] by

$ python -m websockets --version
0

The play[] coroutine will reuse much of the code you wrote in the first part of the tutorial

Try to implement this by yourself

Keep in mind that you must restart the WebSocket server, reload the page to start a new game with the first player, copy the JOIN link, and join the game with the second player when you make changes

When play[] works, you can play the game from two separate browsers, possibly running on separate computers on the same local network

A complete solution is available at the bottom of this document

Watch a game

Let's add one more feature. allow spectators to watch the game

The process for inviting a spectator can be the same as for inviting the second player. You will have to duplicate all the initialization logic

Once the initialization sequence is done, watching a game is as simple as registering the WebSocket connection in the connected set in order to receive game events and doing nothing until the spectator disconnects. You can wait for a connection to terminate with wait_closed[]

$ python -m websockets --version
1

The connection can terminate because the receiveMoves[] function closed it explicitly after receiving a "win" event, because the spectator closed their browser, or because the network failed

Again, try to implement this by yourself

When watch[] works, you can invite spectators to watch the game from other browsers, as long as they're on the same local network

Để cải thiện hơn nữa, bạn có thể hỗ trợ thêm khán giả khi trận đấu đang diễn ra. This requires replaying moves that were played before the spectator was added to the connected set. Past moves are available in the moves attribute of the game

Tính năng này được bao gồm trong giải pháp được đề xuất bên dưới

Broadcast

When you need to send a message to the two players and to all spectators, you're using this pattern

$ pip install websockets
7

Vì đây là một mẫu rất phổ biến trong các máy chủ WebSocket, nên websockets cung cấp trình trợ giúp quảng bá [] cho mục đích này

$ python -m websockets --version
3

Calling broadcast[] once is more efficient than calling send[] in a loop

Tuy nhiên, có một sự khác biệt tinh tế trong hành vi. Did you notice that there's no await in the second version? Indeed, broadcast[] is a function, not a coroutine like send[] or recv[]

It's quite obvious why recv[] is a coroutine. When you want to receive the next message, you have to wait until the client sends it and the network transmits it

It's less obvious why send[] is a coroutine. If you send many messages or large messages, you could write data faster than the network can transmit it or the client can read it. Then, outgoing data will pile up in buffers, which will consume memory and may crash your application

To avoid this problem, send[] waits until the write buffer drains. By slowing down the application as necessary, this ensures that the server doesn't send data too quickly. Điều này được gọi là áp suất ngược và nó hữu ích để xây dựng các hệ thống mạnh mẽ

That said, when you're sending the same messages to many clients in a loop, applying backpressure in this way can become counterproductive. When you're broadcasting, you don't want to slow down everyone to the pace of the slowest clients; you want to drop clients that cannot keep up with the data stream. That's why broadcast[] doesn't wait until write buffers drain

For our Connect Four game, there's no difference in practice. the total amount of data sent on a connection for a game of Connect Four is less than 64 KB, so the write buffer never fills up and backpressure never kicks in anyway

Summary

In this second part of the tutorial, you learned how to

You can now play a Connect Four game from separate browser, communicating over WebSocket connections with a server that synchronizes the game logic

However, the two players have to be on the same local network as the server, so the constraint of being in the same place still mostly applies

Head over to the third part of the tutorial to deploy the game to the web and remove this constraint

Solution

app. py

$ python -m websockets --version
4

index. html

$ python -m websockets --version
5

main. js

$ python -m websockets --version
6

Part 3 - Deploy to the web

This is the third part of the tutorial

In the first and second parts of the tutorial, for local development, you ran a HTTP server on http. //localhost. 8000/ with

$ python -m http.server

and a WebSocket server on ws. //máy chủ cục bộ. 8001/ with

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
0

Now you want to deploy these servers on the Internet. There's a vast range of hosting providers to choose from. For the sake of simplicity, we'll rely on

  • GitHub Pages for the HTTP server;
  • Heroku cho máy chủ WebSocket

Commit project to git

Có lẽ bạn đã cam kết công việc của mình với git trong khi bạn đang tiến hành thông qua hướng dẫn. If you didn't, now is a good time, because GitHub and Heroku offer git-based deployment workflows

Khởi tạo một kho lưu trữ git

$ python -m websockets --version
9

Thêm tất cả các tệp và cam kết




Connect Four




0

Chuẩn bị máy chủ WebSocket

Trước khi bạn triển khai máy chủ, bạn phải điều chỉnh nó để đáp ứng các yêu cầu về thời gian chạy của Heroku. Điều này liên quan đến hai thay đổi nhỏ

1. Heroku mong đợi máy chủ lắng nghe trên một cổng cụ thể, được cung cấp trong biến môi trường $PORT. 2. Heroku gửi tín hiệu SIGTERM khi tắt dyno, tín hiệu này sẽ kích hoạt lối thoát sạch

Điều chỉnh main[] coroutine cho phù hợp




Connect Four




1




Connect Four




2

Để bắt tín hiệu SIGTERM, main[] tạo một Tương lai được gọi là dừng và đăng ký một trình xử lý tín hiệu đặt kết quả của tương lai này. Giá trị của tương lai không quan trọng;

Then, by using serve[] as a context manager and exiting the context when stop has a result, main[] ensures that the server closes connections cleanly and exits on SIGTERM

Ứng dụng hiện đã hoàn toàn tương thích với Heroku

Deploy the WebSocket server

Tạo một yêu cầu. txt với nội dung này để cài đặt websockets khi xây dựng hình ảnh




Connect Four




3

Heroku xử lý các yêu cầu. txt làm tín hiệu để phát hiện ứng dụng Python

That's why you don't need to declare that you need a Python runtime

Tạo 1 file Procfile với nội dung này để cấu hình câu lệnh chạy server




Connect Four




4

Cam kết thay đổi của bạn




Connect Four




5

Follow the set-up instructions to install the Heroku CLI and to log in, if you haven't done that yet

Tạo một ứng dụng Heroku. You must choose a unique name and replace websockets-tutorial by this name in the following command




Connect Four




6

Nếu bạn sử dụng lại tên mà người khác đã sử dụng, bạn sẽ nhận được lỗi này;




Connect Four




7

Triển khai bằng cách đẩy mã lên Heroku




Connect Four




8

Bạn có thể kiểm tra máy chủ WebSocket với máy khách tương tác chính xác như bạn đã làm trong phần đầu của hướng dẫn. Thay websockets-tutorial bằng tên ứng dụng của bạn trong lệnh sau




Connect Four




9

nó hoạt động

Prepare the web application

Trước khi bạn triển khai ứng dụng web, có lẽ bạn đang tự hỏi nó sẽ định vị máy chủ WebSocket như thế nào? . js

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
6

Bạn có thể thực hiện chiến lược này thêm một bước nữa bằng cách kiểm tra địa chỉ của máy chủ HTTP và xác định địa chỉ của máy chủ WebSocket tương ứng

Thêm chức năng này vào chính. js;

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
1

Sau đó, hãy cập nhật quá trình khởi tạo để kết nối với địa chỉ này

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
2

Cam kết thay đổi của bạn

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
3

Deploy the web application

Truy cập GitHub và tạo một kho lưu trữ mới có tên websockets-tutorial

Đẩy mã của bạn vào kho lưu trữ này. Bạn phải thay thế aaugustin bằng tên người dùng GitHub của mình trong lệnh sau

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
4

Quay lại GitHub, mở tab Cài đặt của kho lưu trữ và chọn Trang trong menu. Select the main branch as source and click Save. GitHub cho bạn biết rằng trang web của bạn đã được xuất bản

Theo liên kết và bắt đầu một trò chơi

Summary

Trong phần thứ ba của hướng dẫn này, bạn đã học cách triển khai ứng dụng WebSocket với Heroku

Bạn có thể bắt đầu trò chơi Connect Four, gửi liên kết THAM GIA cho bạn bè và chơi qua Internet

Chúc mừng bạn đã hoàn thành hướng dẫn. Tận hưởng việc xây dựng các ứng dụng web thời gian thực với websocket

Solution

app. py

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
5

index. html

$ python -m websockets --version
5

main. js

import { createBoard, playMove } from "./connect4.js";
window.addEventListener["DOMContentLoaded", [] => {

// Initialize the UI.
const board = document.querySelector[".board"];
createBoard[board]; }];
7

hồ sơ




Connect Four




4

yêu cầu. txt




Connect Four




3

Đang vội?

Xem hướng dẫn bắt đầu nhanh

LÀM THẾ NÀO ĐỂ HƯỚNG DẪN

Đang vội?

Bắt đầu nhanh

Dưới đây là một vài ví dụ để giúp bạn bắt đầu nhanh chóng với websockets

Nói "Xin chào thế giới. "

Đây là một máy chủ WebSocket

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

người phục vụ. py

$ python -m http.server
0

serve[] thực thi trình xử lý kết nối coroutine hello[] một lần cho mỗi kết nối WebSocket. It closes the WebSocket connection when the handler returns

Here's a corresponding WebSocket client

It sends a name to the server, receives a greeting, and closes the connection

client. py

$ python -m http.server
1

Using connect[] as an asynchronous context manager ensures the WebSocket connection is closed

Encrypt connections

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 TLS [Transport Layer Security]. wss requires certificates like https

TLS vs. SSL

TLS is sometimes referred to as SSL [Secure Sockets Layer]. SSL was an earlier encryption protocol; the name stuck

Here's how to adapt the server to encrypt connections. You must download localhost. pem and save it in the same directory as server_secure. py

See the documentation of the ssl module for details on configuring the TLS context securely

server_secure. py

$ python -m http.server
2

Here's how to adapt the client similarly

client_secure. py

$ python -m http.server
3

In this example, the client needs a TLS context because the server uses a self-signed certificate

When connecting to a secure WebSocket server with a valid certificate — any certificate signed by a CA that your Python installation trusts — you can simply pass ssl=True to connect[]

Connect from a browser

The WebSocket protocol was invented for the web — as the name says

Here's how to connect to a WebSocket server from a browser

Run this script in a console

show_time. py

$ python -m http.server
4

Save this file as show_time. html

show_time. html

$ python -m http.server
5

Save this file as show_time. js

show_time. js

$ python -m http.server
6

Then, open show_time. html in several browsers. Clocks tick irregularly

Broadcast messages

Let's change the previous example to send the same timestamps to all browsers, instead of generating independent sequences for each client

Stop the previous script if it's still running and run this script in a console

show_time_2. py

$ python -m http.server
7

Refresh show_time. html in all browsers. Clocks tick in sync

Manage application state

A WebSocket server can receive events from clients, process them to update the application state, and broadcast the updated state to all connected clients

Here's an example where any client can increment or decrement a counter. The concurrency model of asyncio guarantees that updates are serialized

Run this script in a console

counter. py

$ python -m http.server
8

Save this file as counter. html

counter. html

$ python -m http.server
9

Save this file as counter. css

counter. css

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
0

Save this file as counter. js

counter. js

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
1

Then open counter. html file in several browsers and play with [+] and [-]

If you're stuck, perhaps you'll find the answer here

Cheat sheet

Server

•Write a coroutine that handles a single connection. It receives a WebSocket protocol instance and the URI path in argument

  • Call recv[] and send[] to receive and send messages at any time
  • When recv[] or send[] raises ConnectionClosed, clean up and exit. If you started other asyncio. Task, terminate them before exiting
  • If you aren't awaiting recv[], consider awaiting wait_closed[] to detect quickly when the connection is closed
  • You may ping[] or pong[] if you wish but it isn't needed in general

•Create a server with serve[] which is similar to asyncio's create_server[]. You can also use it as an asynchronous context manager

  • The server takes care of establishing connections, then lets the handler execute the application logic, and finally closes the connection after the handler exits normally or with an exception
  • For advanced customization, you may subclass WebSocketServerProtocol and pass either this subclass or a factory function as the create_protocol argument

Client

•Create a client with connect[] which is similar to asyncio's create_connection[]. You can also use it as an asynchronous context manager

•For advanced customization, you may subclass WebSocketClientProtocol and pass either this subclass or a factory function as the create_protocol argument

  • Call recv[] and send[] to receive and send messages at any time
  • You may ping[] or pong[] if you wish but it isn't needed in general
  • If you aren't using connect[] as a context manager, call close[] to terminate the connection

Debugging

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

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
2

The logs contain

  • Exceptions in the connection handler at the ERROR level
  • Exceptions in the opening or closing handshake at the INFO level
  • All frames at the DEBUG level — this can be very verbose

If you're new to asyncio, you will certainly encounter issues that are related to asynchronous programming in general rather than to websockets in particular. Fortunately Python's official documentation provides advice to develop with asyncio. Check it out. it's invaluable

Patterns

Here are typical patterns for processing messages in a WebSocket server or client. You will certainly implement some of them in your application

This page gives examples of connection handlers for a server. However, they're also applicable to a client, simply by assuming that websocket is a connection created with connect[]

Kết nối WebSocket tồn tại lâu dài. You will usually write a loop to process several messages during the lifetime of a connection

Consumer

To receive messages from the WebSocket connection

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
3

In this example, consumer[] is a coroutine implementing your business logic for processing a message received on the WebSocket connection. Each message may be str or bytes

Iteration terminates when the client disconnects

Producer

To send messages to the WebSocket connection

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
4

In this example, producer[] is a coroutine implementing your business logic for generating the next message to send on the WebSocket connection. Each message must be str or bytes

Iteration terminates when the client disconnects because send[] raises a ConnectionClosed exception, which breaks out of the while True loop

Consumer and producer

You can receive and send messages on the same WebSocket connection by combining the consumer and producer patterns. This requires running two tasks in parallel

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
5

If a task terminates, gather[] doesn't cancel the other task. Điều này có thể dẫn đến tình trạng nhà sản xuất tiếp tục chạy sau khi người tiêu dùng hoàn thành, điều này có thể làm rò rỉ tài nguyên

Here's a way to exit and close the WebSocket connection as soon as a task terminates, after canceling the other task

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
6

Registration

To keep track of currently connected clients, you can register them when they connect and unregister them when they disconnect

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
7

This example maintains the set of connected clients in memory. This works as long as you run a single process. It doesn't scale to multiple processes

Publish–subscribe

If you plan to run multiple processes and you want to communicate updates between processes, then you must deploy a messaging system. You may find publish-subscribe functionality useful

A complete implementation of this idea with Redis is described in the Django integration guide

Reload on code changes

When developing a websockets server, you may run it locally to test changes. Unfortunately, whenever you want to try a new version of the code, you must stop the server and restart it, which slows down your development process

Web frameworks such as Django or Flask provide a development server that reloads the application automatically when you make code changes. There is no such functionality in websockets because it's designed for production rather than development

However, you can achieve the same result easily

Install watchdog with the watchmedo shell utility

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
8

Run your server with watchmedo auto-restart

#!/usr/bin/env python
import asyncio
import websockets
async def handler[websocket]:

while True:
message = await websocket.recv[]
print[message] async def main[]:
async with websockets.serve[handler, "", 8001]:
await asyncio.Future[] # run forever if __name__ == "__main__":
asyncio.run[main[]]
9

This example assumes that the server is defined in a script called app. py. Điều chỉnh nó khi cần thiết

Hướng dẫn này sẽ giúp bạn tích hợp ổ cắm web vào một hệ thống rộng lớn hơn

Tích hợp với Django

If you're looking at adding real-time capabilities to a Django project with WebSocket, you have two main options

1. Sử dụng Kênh Django, một dự án thêm WebSocket vào Django, trong số các tính năng khác. Cách tiếp cận này được hỗ trợ đầy đủ bởi Django. However, it requires switching to a new deployment architecture. 2. Deploying a separate WebSocket server next to your Django project. Kỹ thuật này rất phù hợp khi bạn cần thêm một nhóm nhỏ các tính năng thời gian thực — có thể là dịch vụ thông báo — vào ứng dụng HTTP

Hướng dẫn này chỉ ra cách triển khai kỹ thuật thứ hai với websockets. It assumes familiarity with Django

xác thực kết nối

Vì máy chủ websockets chạy bên ngoài Django nên chúng tôi cần tích hợp nó với django. contrib. auth

We will generate authentication tokens in the Django project. Then we will send them to the websockets server, where they will authenticate the user

Generating a token for the current user and making it available in the browser is up to you. Bạn có thể hiển thị mã thông báo trong mẫu hoặc tìm nạp mã thông báo đó bằng lệnh gọi API

Tham khảo hướng dẫn chủ đề về xác thực để biết chi tiết về thiết kế này

Generate tokens

We want secure, short-lived tokens containing the user ID. We'll rely on django-sesame, a small library designed exactly for this purpose

Add django-sesame to the dependencies of your Django project, install it, and configure it in the settings of the project

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
00

[If your project already uses another authentication backend than the default "django. contrib. auth. backends. ModelBackend", adjust accordingly. ]

You don't need "sesame. middleware. AuthenticationMiddleware". It is for authenticating users in the Django server, while we're authenticating them in the websockets server

We'd like our tokens to be valid for 30 seconds. We expect web pages to load and to establish the WebSocket connection within this delay. Configure django-sesame accordingly in the settings of your Django project

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
01

If you expect your web site to load faster for all clients, a shorter lifespan is possible. However, in the context of this document, it would make manual testing more difficult

You could also enable single-use tokens. However, this would update the last login date of the user every time a WebSocket connection is established. This doesn't seem like a good idea, both in terms of behavior and in terms of performance

Now you can generate tokens in a django-admin shell as follows

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
02

Keep this console open. since tokens expire after 30 seconds, you'll have to generate a new token every time you want to test connecting to the server

Validate tokens

Hãy chuyển sang máy chủ websockets

Add websockets to the dependencies of your Django project and install it. Indeed, we're going to reuse the environment of the Django project, so we can call its APIs in the websockets server

Now here's how to implement authentication

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
03

Let's unpack this code

We're calling django. setup[] before doing anything with Django because we're using Django in a standalone script. This assumes that the DJANGO_SETTINGS_MODULE environment variable is set to the Python path to your settings module

The connection handler reads the first message received from the client, which is expected to contain a django-sesame token. Then it authenticates the user with get_user[], the API for authentication outside a view. If authentication fails, it closes the connection and exits

When we call an API that makes a database query such as get_user[], we wrap the call in to_thread[]. Indeed, the Django ORM doesn't support asynchronous I/O. It would block the event loop if it didn't run in a separate thread. to_thread[] is available since Python 3. 9. In earlier versions, use run_in_executor[] instead

Finally, we start a server with serve[]

We're ready to test

Save this code to a file called authentication. py, make sure the DJANGO_SETTINGS_MODULE environment variable is set properly, and start the websockets server

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
04

Generate a new token — remember, they're only valid for 30 seconds — and use it to connect to your server. Paste your token and press Enter when you get a prompt

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
05

nó hoạt động

If you enter an expired or invalid token, authentication fails and the server closes the connection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
06

You can also test from a browser by generating a new token and running the following code in the JavaScript console of the browser

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
07

If you don't want to import your entire Django project into the websockets server, you can build a separate Django project with django. contrib. auth, django-sesame, a suitable User model, and a subset of the settings of the main project

Stream events

We can connect and authenticate but our server doesn't do anything useful yet

Let's send a message every time a user makes an action in the admin. This message will be broadcast to all users who can access the model on which the action was made. This may be used for showing notifications to other users

Many use cases for WebSocket with Django follow a similar pattern

Set up event bus

We need a event bus to enable communications between Django and websockets. Both sides connect permanently to the bus. Then Django writes events and websockets reads them. For the sake of simplicity, we'll rely on Redis Pub/Sub

The easiest way to add Redis to a Django project is by configuring a cache backend with django-redis. This library manages connections to Redis efficiently, persisting them between requests, and provides an API to access the Redis connection directly

Install Redis, add django-redis to the dependencies of your Django project, install it, and configure it in the settings of the project

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
08

If you already have a default cache, add a new one with a different name and change get_redis_connection["default"] in the code below to the same name

Publish events

Now let's write events to the bus

Add the following code to a module that is imported when your Django project starts. Typically, you would put it in a signals. py module, which you would import in the AppConfig. ready[] method of one of your apps

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
09

This code runs every time the admin saves a LogEntry object to keep track of a change. It extracts interesting data, serializes it to JSON, and writes an event to Redis

Let's check that it works

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
10

Leave this command running, start the Django development server and make changes in the admin. add, modify, or delete objects. You should see corresponding events published to the "events" stream

Broadcast events

Now let's turn to reading events and broadcasting them to connected clients. We need to add several features

  • Keep track of connected clients so we can broadcast messages
  • Tell which content types the user has permission to view or to change
  • Connect to the message bus and read events
  • Broadcast these events to users who have corresponding permissions

Here's a complete implementation

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
11

Since the get_content_types[] function makes a database query, it is wrapped inside asyncio. to_thread[]. It runs once when each WebSocket connection is open; then its result is cached for the lifetime of the connection. Indeed, running it for each message would trigger database queries for all connected users at the same time, which would hurt the database

The connection handler merely registers the connection in a global variable, associated to the list of content types for which events should be sent to that connection, and waits until the client disconnects

The process_events[] function reads events from Redis and broadcasts them to all connections that should receive them. We don't care much if a sending a notification fails — this happens when a connection drops between the moment we iterate on connections and the moment the corresponding message is sent — so we start a task with for each message and forget about it. Also, this means we're immediately ready to process the next event, even if it takes time to send a message to a slow client

Since Redis can publish a message to multiple subscribers, multiple instances of this server can safely run in parallel

Does it scale?

In theory, given enough servers, this design can scale to a hundred million clients, since Redis can handle ten thousand servers and each server can handle ten thousand clients. In practice, you would need a more scalable message bus before reaching that scale, due to the volume of messages

The WebSocket protocol makes provisions for extending or specializing its features, which websockets supports fully

Write an extension

During the opening handshake, WebSocket clients and servers negotiate which extensions will be used with which parameters. Then each frame is processed by extensions before being sent or after being received

As a consequence, writing an extension requires implementing several classes

  • Extension Factory. it negotiates parameters and instantiates the extension

    Clients and servers require separate extension factories with distinct APIs

    Extension factories are the public API of an extension

  • Extension. it decodes incoming frames and encodes outgoing frames

    If the extension is symmetrical, clients and servers can use the same class

    Extensions are initialized by extension factories, so they don't need to be part of the public API of an extension

websockets provides base classes for extension factories and extensions. See ClientExtensionFactory, ServerExtensionFactory, and Extension for details

Once your application is ready, learn how to deploy it on various platforms

Deploy to Render

This guide describes how to deploy a websockets server to Render

The free plan of Render is sufficient for trying this guide

However, on a free plan, connections are dropped after five minutes, which is quite short for WebSocket application

We're going to deploy a very simple app. The process would be identical for a more realistic app

Create repository

Deploying to Render requires a git repository. Let's initialize one

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
12

Render requires the git repository to be hosted at GitHub or GitLab

Sign up or log in to GitHub. Create a new repository named websockets-echo. Don't enable any of the initialization options offered by GitHub. Then, follow instructions for pushing an existing repository from the command line

After pushing, refresh your repository's homepage on GitHub. You should see an empty repository with an empty initial commit

Create application

Here's the implementation of the app, an echo server. Save it in a file called app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
13

This app implements requirements for zero downtime deploys

  • it provides a health check at /healthz;
  • it closes connections and exits cleanly when it receives a SIGTERM signal

Create a requirements. txt file containing this line to declare a dependency on websockets




Connect Four




3

Confirm that you created the correct files and commit them to git

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
15

Push the changes to GitHub

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
16

The app is ready. Let's deploy it

Deploy application

Sign up or log in to Render

Create a new web service. Connect the git repository that you just created

Then, finalize the configuration of your app as follows

  • Name. websockets-echo
  • Start Command. python app. py

If you're just experimenting, select the free plan. Create the web service

To configure the health check, go to Settings, scroll down to Health & Alerts, and set

•Health Check Path. /healthz

This triggers a new deployment

Validate deployment

Let's confirm that your application is running as expected

Since it's a WebSocket server, you need a WebSocket client, such as the interactive client that comes with websockets

If you're currently building a websockets server, perhaps you're already in a virtualenv where websockets is installed. If not, you can install it in a new virtualenv as follows

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
17

Connect the interactive client — you must replace websockets-echo with the name of your Render app in this command

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
18

Great. Your app is running

Once you're connected, you can send any message and the server will echo it, or press Ctrl-D to terminate the connection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
19

You can also confirm that your application shuts down gracefully when you deploy a new version. Due to limitations of Render's free plan, you must upgrade to a paid plan before you perform this test

Connect an interactive client again — remember to replace websockets-echo with your app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
18

Trigger a new deployment with Manual Deploy > Deploy latest commit. When the deployment completes, the connection is closed with code 1001 [going away]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
21

If graceful shutdown wasn't working, the server wouldn't perform a closing handshake and the connection would be closed with code 1006 [connection closed abnormally]

Remember to downgrade to a free plan if you upgraded just for testing this feature

Deploy to Fly

This guide describes how to deploy a websockets server to Fly

The free tier of Fly is sufficient for trying this guide

The free tier include up to three small VMs. This guide uses only one

We're going to deploy a very simple app. The process would be identical for a more realistic app

Create application

Here's the implementation of the app, an echo server. Save it in a file called app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
13

This app implements typical requirements for running on a Platform as a Service

  • it provides a health check at /healthz;
  • it closes connections and exits cleanly when it receives a SIGTERM signal

Create a requirements. txt file containing this line to declare a dependency on websockets




Connect Four




3

The app is ready. Let's deploy it

Deploy application

Follow the instructions to install the Fly CLI, if you haven't done that yet

Sign up or log in to Fly

Launch the app — you'll have to pick a different name because I'm already using websockets-echo

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
24

This will build the image with a generic buildpack

Fly can build images with a Dockerfile or a buildpack. Here, fly launch configures a generic Paketo buildpack

If you'd rather package the app with a Dockerfile, check out the guide to containerize an application

Replace the auto-generated fly. toml with

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
25

This configuration

  • listens on port 443, terminates TLS, and forwards to the app on port 8080;
  • declares a health check at /healthz;
  • requests a SIGTERM for terminating the app

Replace the auto-generated Procfile with




Connect Four




4

This tells Fly how to run the app

Now you can deploy it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
27

Validate deployment

Let's confirm that your application is running as expected

Since it's a WebSocket server, you need a WebSocket client, such as the interactive client that comes with websockets

If you're currently building a websockets server, perhaps you're already in a virtualenv where websockets is installed. If not, you can install it in a new virtualenv as follows

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
17

Connect the interactive client — you must replace websockets-echo with the name of your Fly app in this command

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
29

Great. Your app is running

Once you're connected, you can send any message and the server will echo it, or press Ctrl-D to terminate the connection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
19

You can also confirm that your application shuts down gracefully

Connect an interactive client again — remember to replace websockets-echo with your app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
29

In another shell, restart the app — again, replace websockets-echo with your app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
32

Go back to the first shell. The connection is closed with code 1001 [going away]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
33

If graceful shutdown wasn't working, the server wouldn't perform a closing handshake and the connection would be closed with code 1006 [connection closed abnormally]

Triển khai lên Heroku

This guide describes how to deploy a websockets server to Heroku. The same principles should apply to other Platform as a Service providers

Heroku no longer offers a free tier

When this tutorial was written, in September 2021, Heroku offered a free tier where a websockets app could run at no cost. In November 2022, Heroku removed the free tier, making it impossible to maintain this document. As a consequence, it isn't updated anymore and may be removed in the future

We're going to deploy a very simple app. The process would be identical for a more realistic app

Create repository

Deploying to Heroku requires a git repository. Let's initialize one

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
34

Create application

Here's the implementation of the app, an echo server. Save it in a file called app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
35

Heroku expects the server to listen on a specific port, which is provided in the $PORT environment variable. The app reads it and passes it to serve[]

Heroku sends a SIGTERM signal to all processes when shutting down a dyno. When the app receives this signal, it closes connections and exits cleanly

Create a requirements. txt file containing this line to declare a dependency on websockets




Connect Four




3

Create a Procfile




Connect Four




4

This tells Heroku how to run the app

Confirm that you created the correct files and commit them to git

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
38

The app is ready. Let's deploy it

Deploy application

Follow the instructions to install the Heroku CLI, if you haven't done that yet

Sign up or log in to Heroku

Create a Heroku app — you'll have to pick a different name because I'm already using websockets-echo

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
39

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
40

Validate deployment

Let's confirm that your application is running as expected

Since it's a WebSocket server, you need a WebSocket client, such as the interactive client that comes with websockets

If you're currently building a websockets server, perhaps you're already in a virtualenv where websockets is installed. If not, you can install it in a new virtualenv as follows

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
17

Connect the interactive client — you must replace websockets-echo with the name of your Heroku app in this command

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
42

Great. Your app is running

Once you're connected, you can send any message and the server will echo it, or press Ctrl-D to terminate the connection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
19

You can also confirm that your application shuts down gracefully

Connect an interactive client again — remember to replace websockets-echo with your app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
42

In another shell, restart the app — again, replace websockets-echo with your app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
45

Go back to the first shell. The connection is closed with code 1001 [going away]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
46

If graceful shutdown wasn't working, the server wouldn't perform a closing handshake and the connection would be closed with code 1006 [connection closed abnormally]

Deploy to Kubernetes

This guide describes how to deploy a websockets server to Kubernetes. It assumes familiarity with Docker and Kubernetes

We're going to deploy a simple app to a local Kubernetes cluster and to ensure that it scales as expected

In a more realistic context, you would follow your organization's practices for deploying to Kubernetes, but you would apply the same principles as far as websockets is concerned

Containerize application

Here's the app we're going to deploy. Save it in a file called app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
47

This is an echo server with one twist. every message blocks the server for 100ms, which creates artificial starvation of CPU time. This makes it easier to saturate the server for load testing

The app exposes a health check on /healthz. It also provides two other endpoints for testing purposes. /inemuri will make the app unresponsive for 10 seconds and /seppuku will terminate it

The quest for the perfect Python container image is out of scope of this guide, so we'll go for the simplest possible configuration instead

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
48

After saving this Dockerfile, build the image

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
49

Test your image by running

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
50

Then, in another shell, in a virtualenv where websockets is installed, connect to the app and check that it echoes anything you send

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
51

Now, in yet another shell, stop the app with

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
52

Going to the shell where you connected to the app, you can confirm that it shut down gracefully

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
53

If it didn't, you'd get code 1006 [connection closed abnormally]

Deploy application

Configuring Kubernetes is even further beyond the scope of this guide, so we'll use a basic configuration for testing, with just one Service and one Deployment

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
54

For local testing, a service of type NodePort is good enough. Để triển khai vào sản xuất, bạn sẽ định cấu hình Ingress

After saving this to a file called deployment. yaml, you can deploy

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
55

Now you have a deployment with one pod running

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
56

You can connect to the service — press Ctrl-D to exit

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
57

Validate deployment

First, let's ensure the liveness probe works by making the app unresponsive

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
58

Since we have only one pod, we know that this pod will go to sleep

The liveness probe is configured to run every second. By default, liveness probes time out after one second and have a threshold of three failures. Therefore Kubernetes should restart the pod after at most 5 seconds

Indeed, after a few seconds, the pod reports a restart

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
59

Next, let's take it one step further and crash the app

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
60

The pod reports a second restart

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
61

All good — Kubernetes delivers on its promise to keep our app alive

Scale deployment

Of course, Kubernetes is for scaling. Let's scale — modestly — to 10 pods

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
62

After a few seconds, we have 10 pods running

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
63

Now let's generate load. We'll use this script

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
64

We'll connect 500 clients in parallel, meaning 50 clients per pod, and have each client send 6 messages. Since the app blocks for 100ms before responding, if connections are perfectly distributed, we expect a total run time slightly over 50 * 6 * 0. 1 = 30 seconds

Let's try it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
65

A total runtime of 36 seconds is in the right ballpark. Repeating this experiment with other parameters shows roughly consistent results, with the high variability you'd expect from a quick benchmark without any effort to stabilize the test setup

Finally, we can scale back to one pod

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
66

Deploy with Supervisor

This guide proposes a simple way to deploy a websockets server directly on a Linux or BSD operating system

We'll configure Supervisor to run several server processes and to restart them if needed

We'll bind all servers to the same port. The OS will take care of balancing connections

Create and activate a virtualenv

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
67

Cài đặt ổ cắm web và Trình giám sát

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
68

Save this app to a file called app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
69

This is an echo server with two features added for the purpose of this guide

  • It shuts down gracefully when receiving a SIGTERM signal;
  • It enables the reuse_port option of create_server[], which in turns sets SO_REUSEPORT on the accept socket

Save this Supervisor configuration to supervisord. conf

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
70

This is the minimal configuration required to keep four instances of the app running, restarting them if they exit

Now start Supervisor in the foreground

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
71

In another shell, after activating the virtualenv, we can connect to the app — press Ctrl-D to exit

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
72

Look at the pid of an instance of the app in the logs and terminate it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
73

The logs show that Supervisor restarted this instance

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
74

Now let's check what happens when we shut down Supervisor, but first let's establish a connection and leave it open

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
75

Look at the pid of supervisord itself in the logs and terminate it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
76

The logs show that Supervisor terminated all instances of the app before exiting

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
77

And you can see that the connection to the app was closed gracefully

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
78

In this example, we've been sharing the same virtualenv for supervisor and websockets

In a real deployment, you would likely

  • Install Supervisor with the package manager of the OS
  • Create a virtualenv dedicated to your application
  • Add environment=PATH="path/to/your/virtualenv/bin" in the Supervisor configuration. Then python app. py runs in that virtualenv

Deploy behind nginx

This guide demonstrates a way to load balance connections across multiple websockets server processes running on the same machine with nginx

We'll run server processes with Supervisor as described in this guide

Run server processes

Save this app to app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
79

We'd like to nginx to connect to websockets servers via Unix sockets in order to avoid the overhead of TCP for communicating between processes running in the same OS

We start the app with unix_serve[]. Each server process listens on a different socket thanks to an environment variable set by Supervisor to a different value

Save this configuration to supervisord. conf

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
70

This configuration runs four instances of the app

Install Supervisor and run it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
81

Configure and run nginx

Here's a simple nginx configuration to load balance connections across four processes

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
82

We set daemon off so we can run nginx in the foreground for testing

Then we combine the WebSocket proxying and load balancing guides

  • The WebSocket protocol requires HTTP/1. 1. Chúng ta phải đặt phiên bản giao thức HTTP thành 1. 1, else nginx defaults to HTTP/1. 0 for proxying
  • The WebSocket handshake involves the Connection and Upgrade HTTP headers. We must pass them to the upstream explicitly, else nginx drops them because they're hop-by-hop headers

    We deviate from the WebSocket proxying guide because its example adds a Connection. Upgrade header to every upstream request, even if the original request didn't contain that header

  • In the upstream configuration, we set the load balancing method to least_conn in order to balance the number of active connections across servers. This is best for long running connections

Save the configuration to nginx. conf, install nginx, and run it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
83

You can confirm that nginx proxies connections properly

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
84

Deploy behind HAProxy

This guide demonstrates a way to load balance connections across multiple websockets server processes running on the same machine with HAProxy

We'll run server processes with Supervisor as described in this guide

Run server processes

Save this app to app. py

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
85

Each server process listens on a different port by extracting an incremental index from an environment variable set by Supervisor

Save this configuration to supervisord. conf

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
70

This configuration runs four instances of the app

Install Supervisor and run it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
81

Configure and run HAProxy

Here's a simple HAProxy configuration to load balance connections across four processes

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
88

In the backend configuration, we set the load balancing method to leastconn in order to balance the number of active connections across servers. This is best for long running connections

Save the configuration to haproxy. cfg, install HAProxy, and run it

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
89

You can confirm that HAProxy proxies connections properly

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
84

If you're integrating the Sans-I/O layer of websockets into a library, rather than building an application with websockets, follow this guide

Integrate the Sans-I/O layer

This guide explains how to integrate the Sans-I/O layer of websockets to add support for WebSocket in another library

As a prerequisite, you should decide how you will handle network I/O and asynchronous control flow

Your integration layer will provide an API for the application on one side, will talk to the network on the other side, and will rely on websockets to implement the protocol in the middle. [image]

Opening a connection

Phía khách hàng

If you're building a client, parse the URI you'd like to connect to

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
91

Open a TCP connection to [wsuri. host, wsuri. port] and perform a TLS handshake if wsuri. secure is True

Initialize a ClientConnection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
92

Create a WebSocket handshake request with connect[] and send it with send_request[]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
93

Then, call data_to_send[] and send its output to the network, as described in Send data below

The first event returned by events_received[] is the WebSocket handshake response

Khi bắt tay không thành công, lý do có sẵn trong handshake_exc

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
94

Else, the WebSocket connection is open

A WebSocket client API usually performs the handshake then returns a wrapper around the network connection and the ClientConnection

Server-side

If you're building a server, accept network connections from clients and perform a TLS handshake if desired

Đối với mỗi kết nối, hãy khởi tạo ServerConnection

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
95

Sự kiện đầu tiên được trả về bởi events_received[] là yêu cầu bắt tay WebSocket

Create a WebSocket handshake response with accept[] and send it with send_response[]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
96

Ngoài ra, bạn có thể từ chối bắt tay WebSocket và trả lại phản hồi HTTP bằng reject[]

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
97

Then, call data_to_send[] and send its output to the network, as described in Send data below

Ngay cả khi bạn gọi accept[], quá trình bắt tay WebSocket có thể không thành công nếu yêu cầu không chính xác hoặc không được hỗ trợ

Khi bắt tay không thành công, lý do có sẵn trong handshake_exc

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
94

Else, the WebSocket connection is open

A WebSocket server API usually builds a wrapper around the network connection and the ServerConnection. Then it invokes a connection handler that accepts the wrapper in argument

It may also provide a way to close all connections and to shut down the server gracefully

Going forwards, this guide focuses on handling an individual connection

Từ mạng đến ứng dụng

Go through the five steps below until you reach the end of the data stream

Receive data

When receiving data from the network, feed it to the connection's receive_data[] method

When reaching the end of the data stream, call the connection's receive_eof[] method

For example, if sock is a socket

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
99

These methods aren't expected to raise exceptions — unless you call them again after calling receive_eof[], which is an error. [If you get an exception, please file a bug. ]

Send data

Then, call data_to_send[] and send its output to the network

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
00

The empty bytestring signals the end of the data stream. When you see it, you must half-close the TCP connection

Sending data right after receiving data is necessary because websockets responds to ping frames, close frames, and incorrect inputs automatically

Expect TCP connection to close

Closing a WebSocket connection normally involves a two-way WebSocket closing handshake. Sau đó, bất kể việc đóng là bình thường hay bất thường, máy chủ sẽ bắt đầu bắt tay đóng TCP bốn chiều. If the network fails at the wrong point, you can end up waiting until the TCP timeout, which is very long

To prevent dangling TCP connections when you expect the end of the data stream but you never reach it, call close_expected[] and, if it returns True, schedule closing the TCP connection after a short timeout

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
01

If the connection is still open when the timeout elapses, closing the socket makes the execution thread that reads from the socket reach the end of the data stream, possibly with an exception

Close TCP connection

If you called receive_eof[], close the TCP connection now. This is a clean closure because the receive buffer is empty

After receive_eof[] signals the end of the read stream, data_to_send[] always signals the end of the write stream, unless it already ended. So, at this point, the TCP connection is already half-closed. The only reason for closing it now is to release resources related to the socket

Now you can exit the loop relaying data from the network to the application

Receive events

Finally, call events_received[] to obtain events parsed from the data provided to receive_data[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
02

The first event will be the WebSocket opening handshake request or response. See Opening a connection above for details

All later events are WebSocket frames. There are two types of frames

  • Data frames contain messages transferred over the WebSocket connections. You should provide them to the application. See Fragmentation below for how to reassemble messages from frames
  • Control frames provide information about the connection's state. The main use case is to expose an abstraction over ping and pong to the application. Keep in mind that websockets responds to ping frames and close frames automatically. Don't duplicate this functionality

From the application to the network

The connection object provides one method for each type of WebSocket frame

Để gửi một khung dữ liệu

  • gửi_tiếp tục[]
  • send_text[]
  • send_binary[]

These methods raise ProtocolError if you don't set the FIN bit correctly in fragmented messages

For sending a control frame

  • send_close[]
  • send_ping[]
  • send_pong[]

send_close[] initiates the closing handshake. See Closing a connection below for details

If you encounter an unrecoverable error and you must fail the WebSocket connection, call fail[]

After any of the above, call data_to_send[] and send its output to the network, as shown in Send data above

If you called send_close[] or fail[], you expect the end of the data stream. You should follow the process described in Close TCP connection above in order to prevent dangling TCP connections

Closing a connection

Under normal circumstances, when a server wants to close the TCP connection

  • it closes the write side;
  • it reads until the end of the stream, because it expects the client to close the read side;
  • it closes the socket

When a client wants to close the TCP connection

  • it reads until the end of the stream, because it expects the server to close the read side;
  • it closes the write side;
  • it closes the socket

Applying the rules described earlier in this document gives the intended result. As a reminder, the rules are

  • When data_to_send[] returns the empty bytestring, close the write side of the TCP connection
  • When you reach the end of the read stream, close the TCP connection
  • When close_expected[] returns True, if you don't reach the end of the read stream quickly, close the TCP connection

Fragmentation

WebSocket messages may be fragmented. Since this is a protocol-level concern, you may choose to reassemble fragmented messages before handing them over to the application

To reassemble a message, read data frames until you get a frame where the FIN bit is set, then concatenate the payloads of all frames

You will never receive an inconsistent sequence of frames because websockets raises a ProtocolError and fails the connection when this happens. However, you may receive an incomplete sequence if the connection drops in the middle of a fragmented message

Tips

Serialize operations

The Sans-I/O layer expects to run sequentially. If your interact with it from multiple threads or coroutines, you must ensure correct serialization. This should happen automatically in a cooperative multitasking environment

However, you still have to make sure you don't break this property by accident. For example, serialize writes to the network when data_to_send[] returns multiple values to prevent concurrent writes from interleaving incorrectly

Avoid buffers

The Sans-I/O layer doesn't do any buffering. It makes events available in events_received[] as soon as they're received

You should make incoming messages available to the application immediately and stop further processing until the application fetches them. This will usually result in the best performance

FREQUENTLY ASKED QUESTIONS

Many questions asked in websockets' issue tracker are reallyabout asyncio. "

Python's documentation about developing with asyncio is a good complement

Server

Why does the server close the connection prematurely?

Your connection handler exits prematurely. Wait for the work to be finished before returning

For example, if your handler has a structure similar to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
03

change it to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
04

Why does the server close the connection after one message?

Trình xử lý kết nối của bạn thoát sau khi xử lý một tin nhắn. Write a loop to process multiple messages

For example, if your handler looks like this

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
05

change it like this

#!/usr/bin/env python
import asyncio
import websockets
async def echo[websocket]:

async for message in websocket:
await websocket.send[message] async def main[]:
async with websockets.serve[echo, "localhost", 8765]:
await asyncio.Future[] # run forever asyncio.run[main[]]
5

Don't feel bad if this happens to you — it's the most common question in websockets' issue tracker . -]

Why can only one client connect at a time?

Your connection handler blocks the event loop. Look for blocking calls. Any call that may take some time must be asynchronous

For example, if you have

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
07

change it to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
08

This is part of learning asyncio. It isn't specific to websockets

See also Python's documentation about running blocking code

How do I send a message to all users?

Record all connections in a global variable

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
09

Then, call broadcast[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
10

If you're running multiple server processes, make sure you call message_all in each process

How do I send a message to a single user?

Record connections in a global variable, keyed by user identifier

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
11

Then, call send[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
12

Add error handling according to the behavior you want if the user disconnected before the message could be sent

This example supports only one connection per user. To support concurrent connects by the same user, you can change CONNECTIONS to store a set of connections for each user

If you're running multiple server processes, call message_user in each process. The process managing the user's connection sends the message; other processes do nothing

When you reach a scale where server processes cannot keep up with the stream of all messages, you need a better architecture. For example, you could deploy an external publish / subscribe system such as Redis. Server processes would subscribe their clients. Then, they would receive messages only for the connections that they're managing

How do I send a message to a channel, a topic, or some users?

websockets doesn't provide built-in publish / subscribe functionality

Record connections in a global variable, keyed by user identifier, as shown in How do I send a message to a single user?

Then, build the set of recipients and broadcast the message to them, as shown in How do I send a message to all users?

Tích hợp với Django có triển khai đầy đủ mẫu này

Again, as you scale, you may reach the performance limits of a basic in-process implementation. You may need an external publish / subscribe system like Redis

How do I pass arguments to the connection handler?

Bạn có thể liên kết các đối số bổ sung với trình xử lý kết nối bằng funcools. partial[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
13

Một cách khác để đạt được kết quả này là xác định quy trình xử lý coroutine trong phạm vi nơi biến extra_argument tồn tại thay vì tiêm nó thông qua một đối số

How do I access the request path?

It is available in the path attribute

You may route a connection to different handlers depending on the request path

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
14

You may also route the connection based on the first message received from the client, as shown in the tutorial. When you want to authenticate the connection before routing it, this is usually more convenient

Nói chung, đường dẫn yêu cầu trong máy chủ WebSocket ít được chú trọng hơn nhiều so với máy chủ HTTP. Khi máy chủ WebSocket cung cấp một điểm cuối duy nhất, nó có thể hoàn toàn bỏ qua đường dẫn yêu cầu

How do I access HTTP headers?

To access HTTP headers during the WebSocket handshake, you can override process_request

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
15

Sau khi kết nối được thiết lập, các tiêu đề HTTP có sẵn trong request_headers và response_headers

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
16

How do I set HTTP headers?

To set the Sec-WebSocket-Extensions or Sec-WebSocket-Protocol headers in the WebSocket handshake response, use the extensions or subprotocols arguments of serve[]

To override the Server header, use the server_header argument. Set it to None to remove the header

To set other HTTP headers, use the extra_headers argument

How do I get the IP address of the client?

It's available in remote_address

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
17

How do I set the IP addresses my server listens on?

Look at the host argument of create_server[]

serve[] chấp nhận các đối số giống như create_server[]

OSError là gì. [Errno 99] error while attempting to bind on address ['. 1', 80, 0, 0]. address not available mean?

You are calling serve[] without a host argument in a context where IPv6 isn't available

To listen only on IPv4, specify host="0. 0. 0. 0" or family=socket. AF_INET

Refer to the documentation of create_server[] for details

Làm cách nào để đóng kết nối?

websockets takes care of closing the connection when the handler exits

How do I stop a server?

Thoát khỏi trình quản lý bối cảnh phục vụ []

Here's an example that terminates cleanly when it receives SIGTERM on Unix

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
18

How do I run HTTP and WebSocket servers on the same port?

You don't

HTTP and WebSocket have widely different operational characteristics. Running them with the same server becomes inconvenient when you scale

Providing a HTTP server is out of scope for websockets. It only aims at providing a WebSocket server

There's limited support for returning HTTP responses with the process_request hook

If you need more, pick a HTTP server and run it separately

Alternatively, pick a HTTP framework that builds on top of websockets to support WebSocket connections, like Sanic

Client

Why does the client close the connection prematurely?

You're exiting the context manager prematurely. Wait for the work to be finished before exiting

For example, if your code has a structure similar to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
19

change it to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
20

How do I access HTTP headers?

Once the connection is established, HTTP headers are available in request_headers and response_headers

How do I set HTTP headers?

To set the Origin, Sec-WebSocket-Extensions, or Sec-WebSocket-Protocol headers in the WebSocket handshake request, use the origin, extensions, or subprotocols arguments of connect[]

To override the User-Agent header, use the user_agent_header argument. Set it to None to remove the header

To set other HTTP headers, for example the Authorization header, use the extra_headers argument

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
21

Làm cách nào để đóng kết nối?

The easiest is to use connect[] as a context manager

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
22

The connection is closed when exiting the context manager

How do I reconnect when the connection drops?

Use connect[] as an asynchronous iterator

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
23

Make sure you handle exceptions in the async for loop. Uncaught exceptions will break out of the loop

How do I stop a client that is processing messages in a loop?

You can close the connection

Here's an example that terminates cleanly when it receives SIGTERM on Unix

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
24

How do I disable TLS/SSL certificate verification?

Look at the ssl argument of create_connection[]

connect[] accepts the same arguments as create_connection[]

Both sides

What does ConnectionClosedError. no close frame received or sent mean?

If you're seeing this traceback in the logs of a server

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
25

or if a client crashes with this traceback

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
26

it means that the TCP connection was lost. As a consequence, the WebSocket connection was closed without receiving and sending a close frame, which is abnormal

You can catch and handle ConnectionClosed to prevent it from being logged

There are several reasons why long-lived connections may be lost

  • End-user devices tend to lose network connectivity often and unpredictably because they can move out of wireless network coverage, get unplugged from a wired network, enter airplane mode, be put to sleep, etc
  • HTTP load balancers or proxies that aren't configured for long-lived connections may terminate connections after a short amount of time, usually 30 seconds, despite websockets' keepalive mechanism

If you're facing a reproducible issue, enable debug logs to see when and how connections are closed

What does ConnectionClosedError. sent 1011 [unexpected error] keepalive ping timeout; no close frame received mean?

If you're seeing this traceback in the logs of a server

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
27

or if a client crashes with this traceback

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
28

it means that the WebSocket connection suffered from excessive latency and was closed after reaching the timeout of websockets' keepalive mechanism

You can catch and handle ConnectionClosed to prevent it from being logged

There are two main reasons why latency may increase

  • Poor network connectivity
  • More traffic than the recipient can handle

See the discussion of timeouts for details

If websockets' default timeout of 20 seconds is too short for your use case, you can adjust it with the ping_timeout argument

How do I set a timeout on recv[]?

Use wait_for[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
29

This technique works for most APIs, except for asynchronous context managers. See issue 574

How can I pass arguments to a custom protocol subclass?

You can bind additional arguments to the protocol factory with functools. partial[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
30

This example was for a server. The same pattern applies on a client

How do I keep idle connections open?

websockets sends pings at 20 seconds intervals to keep the connection open

Nó sẽ đóng kết nối nếu nó không nhận được pong trong vòng 20 giây

You can adjust this behavior with ping_interval and ping_timeout

See Timeouts for details

How do I respond to pings?

Don't bother; websockets takes care of responding to pings with pongs

asyncio usage

How do I run two coroutines in parallel?

You must start two tasks, which the event loop will run concurrently. You can achieve this with asyncio. gather[] or asyncio. create_task[]

Theo dõi các nhiệm vụ và đảm bảo rằng chúng kết thúc hoặc bạn hủy bỏ chúng khi kết nối kết thúc

Why does my program never receive any messages?

Your program runs a coroutine that never yields control to the event loop. The coroutine that receives messages never gets a chance to run

Putting an await statement in a for or a while loop isn't enough to yield control. Awaiting a coroutine may yield control, but there's no guarantee that it will

For example, send[] only yields control when send buffers are full, which never happens in most practical cases

If you run a loop that contains only synchronous operations and a send[] call, you must yield control explicitly with asyncio. sleep[]

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
31

asyncio. sleep[] always suspends the current task, allowing other tasks to run. This behavior is documented precisely because it isn't expected from every coroutine

See issue 867

Why am I having problems with threads?

You shouldn't use threads. Use tasks instead

Indeed, when you chose websockets, you chose asyncio as the primary framework to handle concurrency. This choice is mutually exclusive with threading

If you believe that you need to run websockets in a thread and some logic in another thread, you should run that logic in a Task instead

If you believe that you cannot run that logic in the same event loop because it will block websockets, run_in_executor[] may help

This question is really about asyncio. Please review the advice about Concurrency and Multithreading in the Python documentation

Why does my simple program misbehave mysteriously?

You are using time. sleep[] instead of asyncio. sleep[], which blocks the event loop and prevents asyncio from operating normally

This may lead to messages getting send but not received, to connection timeouts, and to unexpected results of shotgun debugging e. g. adding an unnecessary call to send[] makes the program functional

Miscellaneous

Why do I get the error. module 'websockets' has no attribute '. '?

Often, this is because you created a script called websockets. py in your current working directory. Then import websockets imports this module instead of the websockets library

Why does my IDE fail to show documentation for websockets APIs?

You are probably using the convenience imports e. g

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
32

This is incompatible with static code analysis. It may break auto-completion and contextual documentation in IDEs, type checking with mypy, etc

Instead, use the real import paths e. g

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
33

Why is websockets slower than another Python library in my benchmark?

Không phải tất cả các thư viện đều đầy đủ tính năng như websockets. For a fair benchmark, you should disable features that the other library doesn't provide. Typically, you may need to disable

  • Compression. set compression=None
  • Keepalive. set ping_interval=None
  • UTF-8 decoding. send bytes rather than str

If websockets is still slower than another Python library, please file a bug

Can I use websockets without async and await?

No, there is no convenient way to do this. You should use another library

Are there onopen, onmessage, onerror, and onclose callbacks?

No, there aren't

websockets provides high-level, coroutine-based APIs. Compared to callbacks, coroutines make it easier to manage control flow in concurrent code

If you prefer callback-based APIs, you should use another library

API REFERENCE

websockets provides client and server implementations, as shown in the getting started guide

The process for opening and closing a WebSocket connection depends on which side you're implementing

  • On the client side, connecting to a server with connect[] yields a connection object that provides methods for interacting with the connection. Your code can open a connection, then send or receive messages

    If you use connect[] as an asynchronous context manager, then websockets closes the connection on exit. If not, then your code is responsible for closing the connection

  • On the server side, serve[] starts listening for client connections and yields an server object that you can use to shut down the server

    Then, when a client connects, the server initializes a connection object and passes it to a handler coroutine, which is where your code can send or receive messages. This pattern is called inversion of control. It's common in frameworks implementing servers

    When the handler coroutine terminates, websockets closes the connection. You may also close it in the handler coroutine if you'd like

Once the connection is open, the WebSocket protocol is symmetrical, except for low-level details that websockets manages under the hood. The same methods are available on client connections created with connect[] and on server connections received in argument by the connection handler of serve[]

Server

asyncio

Starting a server

await websockets. server. serve[ws_handler, host=None, port=None, *, create_protocol=None, logger=None, compression='deflate', origins=None, extensions=None, subprotocols=None, extra_headers=None, server_header='Python/x. y. z websockets/X. Y', process_request=None, select_subprotocol=None, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16, **kwds]Start a WebSocket server listening on host and port

Whenever a client connects, the server creates a WebSocketServerProtocol, performs the opening handshake, and delegates to the connection handler, ws_handler

The handler receives the WebSocketServerProtocol and uses it to send and receive messages

Once the handler completes, either normally or with an exception, the server performs the closing handshake and closes the connection

Awaiting serve[] yields a WebSocketServer. This object provides close[] and wait_closed[] methods for shutting down the server

serve[] can be used as an asynchronous context manager

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
34

The server is shut down automatically when exiting the context

Parameters

  • ws_handler [Union[Callable[[WebSocketServerProtocol], Awaitable[Any]], Callable[[WebSocketServerProtocol, str], Awaitable[Any]]]] -- connection handler. It receives the WebSocket connection, which is a WebSocketServerProtocol, in argument
  • host [Optional[Union[str, Sequence[str]]]] -- network interfaces the server is bound to; see create_server[] for details
  • port [Optional[int]] -- TCP port the server listens on; see create_server[] for details
  • create_protocol [Optional[Callable[[Any], WebSocketServerProtocol]]] -- factory for the asyncio. Giao thức quản lý kết nối;
  • logger [Optional[LoggerLike]] -- logger for this server; defaults to logging. getLogger["websockets. server"]; see the logging guide for details
  • compression [Optional[str]] -- shortcut that enables the "permessage-deflate" extension by default; may be set to None to disable compression; see the compression guide for details
  • origins [Optional[Sequence[Optional[Origin]]]] -- acceptable values of the Origin header; include None in the list if the lack of an origin is acceptable. This is useful for defending against Cross-Site WebSocket Hijacking attacks
  • extensions [Optional[Sequence[ServerExtensionFactory]]] -- list of supported extensions, in order in which they should be tried
  • subprotocols [Optional[Sequence[Subprotocol]]] -- list of supported subprotocols, in order of decreasing preference
  • extra_headers [Union[HeadersLike, Callable[[str, Headers], HeadersLike]]] -- arbitrary HTTP headers to add to the request; this can be a HeadersLike or a callable taking the request path and headers in arguments and returning a HeadersLike
  • server_header [Optional[str]] -- value of the Server response header; defaults to "Python/x. y. z websockets/X. Y"; None removes the header
  • process_request [Optional[Callable[[str, Headers], Awaitable[Optional[Tuple[http. HTTPStatus, HeadersLike, bytes]]]]]] -- intercept HTTP request before the opening handshake; see process_request[] for details
  • select_subprotocol [Optional[Callable[[Sequence[Subprotocol], Sequence[Subprotocol]], Subprotocol]]] -- select a subprotocol supported by the client; see select_subprotocol[] for details

See WebSocketCommonProtocol for the documentation of ping_interval, ping_timeout, close_timeout, max_size, max_queue, read_limit, and write_limit

Any other keyword arguments are passed the event loop's create_server[] method

For example

  • You can set ssl to a SSLContext to enable TLS
  • You can set sock to a socket that you created outside of websockets

ReturnsWebSocket server. Return typeWebSocketServer

await websockets. server. unix_serve[ws_handler, path=None, *, create_protocol=None, logger=None, compression='deflate', origins=None, extensions=None, subprotocols=None, extra_headers=None, server_header='Python/x. y. z websockets/X. Y', process_request=None, select_subprotocol=None, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16, **kwds]Similar to serve[], but for listening on Unix sockets

This function builds upon the event loop's create_unix_server[] method

It is only available on Unix

It's useful for deploying a server behind a reverse proxy such as nginx

Parameterspath [Optional[str]] -- file system path to the Unix socket

Stopping a server

class websockets. server. WebSocketServer[logger=None]WebSocket server returned by serve[]

This class provides the same interface as Server, notably the close[] and wait_closed[] methods

It keeps track of WebSocket connections in order to close them properly when shutting down

Parameterslogger [Optional[LoggerLike]] -- logger for this server; defaults to logging. getLogger["websockets. server"]; see the logging guide for details

close[]Close the server

This method

  • closes the underlying Server;
  • rejects new WebSocket connections with an HTTP 503 [service unavailable] error; this happens when the server accepted the TCP connection but didn't complete the WebSocket opening handshake prior to closing;
  • closes open WebSocket connections with close code 1001 [going away]

close[] is idempotent


await wait_closed[]Wait until the server is closed

When wait_closed[] returns, all TCP connections are closed and all connection handlers have returned

To ensure a fast shutdown, a connection handler should always be awaiting at least one of

  • recv[]. when the connection is closed, it raises ConnectionClosedOK;
  • wait_closed[]. when the connection is closed, it returns

Then the connection handler is immediately notified of the shutdown; it can clean up and exit


get_loop[]Xem asyncio. Server. get_loop[]

is_serving[]See asyncio. Server. is_serving[]

await start_serving[]See asyncio. Server. start_serving[]

Typical use

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
35


await serve_forever[]See asyncio. Server. serve_forever[]

Typical use

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
36

This is an alternative to using serve[] as an asynchronous context manager. Shutdown is triggered by canceling serve_forever[] instead of exiting a serve[] context


socketsSee asyncio. Server. sockets

Using a connection

class websockets. server. WebSocketServerProtocol[ws_handler, ws_server, *, logger=None, origins=None, extensions=None, subprotocols=None, extra_headers=None, server_header='Python/x. y. z websockets/X. Y', process_request=None, select_subprotocol=None, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16]WebSocket server connection

WebSocketServerProtocol provides recv[] and send[] coroutines for receiving and sending messages

It supports asynchronous iteration to receive messages

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
37

The iterator exits normally when the connection is closed with close code 1000 [OK] or 1001 [going away]. It raises a ConnectionClosedError when the connection is closed with any other code

You may customize the opening handshake in a subclass by overriding process_request[] or select_subprotocol[]

Parametersws_server [WebSocketServer] -- WebSocket server that created this connection

See serve[] for the documentation of ws_handler, logger, origins, extensions, subprotocols, extra_headers, and server_header

See WebSocketCommonProtocol for the documentation of ping_interval, ping_timeout, close_timeout, max_size, max_queue, read_limit, and write_limit

await recv[]Receive the next message

When the connection is closed, recv[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure. This is how you detect the end of the message stream

Canceling recv[] is safe. There's no risk of losing the next message. The next invocation of recv[] will return it

This makes it possible to enforce a timeout by wrapping recv[] in wait_for[]

ReturnsA string [str] for a Text frame. A bytestring [bytes] for a Binary frame. Return typeDataRaises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if two coroutines call recv[] concurrently

await send[message]Send a message

A string [str] is sent as a Text frame. A bytestring or bytes-like object [bytes, bytearray, or memoryview] is sent as a Binary frame

send[] also accepts an iterable or an asynchronous iterable of strings, bytestrings, or bytes-like objects to enable fragmentation. Each item is treated as a message fragment and sent in its own frame. All items must be of the same type, or else send[] will raise a TypeError and the connection will be closed

send[] rejects dict-like objects because this is often an error. [If you want to send the keys of a dict-like object as fragments, call its keys[] method and pass the result to send[]. ]

Hủy gửi[] không được khuyến khích. Instead, you should close the connection with close[]. Indeed, there are only two situations where send[] may yield control to the event loop and then get canceled; in both cases, close[] has the same effect and is more clear

1. The write buffer is full. If you don't want to wait until enough data is sent, your only alternative is to close the connection. close[] will likely time out then abort the TCP connection. 2. message is an asynchronous iterator that yields control. Stopping in the middle of a fragmented message will cause a protocol error and the connection will be closed

When the connection is closed, send[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure

Parametersmessage [Union[Data, Iterable[Data], AsyncIterable[Data]] -- message to send. Raises

  • ConnectionClosed -- when the connection is closed
  • TypeError -- if message doesn't have a supported type

await close[code=1000, reason='']Perform the closing handshake

close[] waits for the other end to complete the handshake and for the TCP connection to terminate. As a consequence, there's no need to await wait_closed[] after close[]

close[] is idempotent. it doesn't do anything once the connection is closed

Wrapping close[] in create_task[] is safe, given that errors during connection termination aren't particularly useful

Canceling close[] is discouraged. If it takes too long, you can set a shorter close_timeout. If you don't want to wait, let the Python process exit, then the OS will take care of closing the TCP connection

Parameters

  • code [int] -- WebSocket close code
  • reason [str] -- WebSocket close reason

await wait_closed[]Wait until the connection is closed

This coroutine is identical to the closed attribute, except it can be awaited

This can make it easier to detect connection termination, regardless of its cause, in tasks that interact with the WebSocket connection


await ping[data=None]Send a Ping

A ping may serve as a keepalive, as a check that the remote endpoint received all messages up to this point, or to measure latency

Canceling ping[] is discouraged. If ping[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Canceling the Future returned by ping[] has no effect

Parametersdata [Optional[Data]] -- payload of the ping; a string will be encoded to UTF-8; or None to generate a payload containing four random bytes. ReturnsA future that will be completed when the corresponding pong is received. You can ignore it if you don't intend to wait. The result of the future is the latency of the connection in seconds

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
38

Return typeFuture[float]Raises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if another ping was sent with the same data and
    the corresponding pong wasn't received yet.

await pong[data=b'']Send a Pong

An unsolicited pong may serve as a unidirectional heartbeat

Canceling pong[] is discouraged. If pong[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Parametersdata [Data] -- payload of the pong; a string will be encoded to UTF-8. RaisesConnectionClosed -- when the connection is closed

You can customize the opening handshake in a subclass by overriding these methods

await process_request[path, request_headers]Intercept the HTTP request and return an HTTP response if appropriate

You may override this method in a WebSocketServerProtocol subclass, for example

  • to return a HTTP 200 OK response on a given path; then a load balancer can use this path for a health check;
  • to authenticate the request and return a HTTP 401 Unauthorized or a HTTP 403 Forbidden when authentication fails

You may also override this method with the process_request argument of serve[] and WebSocketServerProtocol. This is equivalent, except process_request won't have access to the protocol instance, so it can't store information for later use

process_request[] dự kiến ​​sẽ hoàn thành nhanh chóng. If it may run for a long time, then it should await wait_closed[] and exit if wait_closed[] completes, or else it could prevent the server from shutting down

Parameters

  • path [str] -- request path, including optional query string
  • request_headers [Headers] -- request headers

ReturnsNone to continue the WebSocket handshake normally

An HTTP response, represented by a 3-uple of the response status, headers, and body, to abort the WebSocket handshake and return that HTTP response instead

Return typeOptional[Tuple[http. HTTPStatus, HeadersLike, bytes]]

select_subprotocol[client_subprotocols, server_subprotocols]Pick a subprotocol among those offered by the client

If several subprotocols are supported by the client and the server, the default implementation selects the preferred subprotocol by giving equal value to the priorities of the client and the server. If no subprotocol is supported by the client and the server, it proceeds without a subprotocol

This is unlikely to be the most useful implementation in practice. Many servers providing a subprotocol will require that the client uses that subprotocol. Such rules can be implemented in a subclass

You may also override this method with the select_subprotocol argument of serve[] and WebSocketServerProtocol

Parameters

  • client_subprotocols [Sequence[Subprotocol]] -- list of subprotocols offered by the client
  • server_subprotocols [Sequence[Subprotocol]] -- list of subprotocols available on the server

ReturnsSelected subprotocol

None to continue without a subprotocol

Return typeOptional[Subprotocol]

WebSocket connection objects also provide these attributes

id. uuid. UUIDUnique identifier of the connection. Useful in logs

logger. LoggerLikeLogger for this connection

property local_address. AnyLocal address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getsockname[]

None if the TCP connection isn't established yet

property remote_address. AnyRemote address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getpeername[]

None if the TCP connection isn't established yet

property open. boolTrue when the connection is open; False otherwise

This attribute may be used to detect disconnections. However, this approach is discouraged per the EAFP principle. Instead, you should handle ConnectionClosed exceptions

property closed. boolTrue when the connection is closed; False otherwise

Be aware that both open and closed are False during the opening and closing sequences

latency. floatLatency of the connection, in seconds

This value is updated after sending a ping frame and receiving a matching pong frame. Trước ping đầu tiên, độ trễ là 0

By default, websockets enables a keepalive mechanism that sends ping frames automatically at regular intervals. You can also send ping frames and measure latency with ping[]

The following attributes are available after the opening handshake, once the WebSocket connection is open

path. strPath of the opening handshake request

request_headers. HeadersOpening handshake request headers

response_headers. HeadersOpening handshake response headers

subprotocol. Optional[Subprotocol]Subprotocol, if one was negotiated

The following attributes are available after the closing handshake, once the WebSocket connection is closed

property close_code. Optional[int]WebSocket close code, defined in section 7. 1. 5 of RFC 6455

None if the connection isn't closed yet

property close_reason. Optional[str]WebSocket close reason, defined in section 7. 1. 6 of RFC 6455

None if the connection isn't closed yet

Basic authentication

websockets supports HTTP Basic Authentication according to RFC 7235 and RFC 7617

websockets. auth. basic_auth_protocol_factory[realm=None, credentials=None, check_credentials=None, create_protocol=None]Protocol factory that enforces HTTP Basic Auth

basic_auth_protocol_factory[] is designed to integrate with serve[] like this

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
39

Parameters

  • realm [Optional[str]] -- indicates the scope of protection. It should contain only ASCII characters because the encoding of non-ASCII characters is undefined. Refer to section 2. 2 of RFC 7235 for details
  • credentials [Optional[Union[Tuple[str, str], Iterable[Tuple[str, str]]]]] -- defines hard coded authorized credentials. It can be a [username, password] pair or a list of such pairs
  • check_credentials [Optional[Callable[[str, str], Awaitable[bool]]]] -- defines a coroutine that verifies credentials. This coroutine receives username and password arguments and returns a bool. One of credentials or check_credentials must be provided but not both
  • create_protocol [Optional[Callable[[Any], BasicAuthWebSocketServerProtocol]]] -- factory that creates the protocol. By default, this is BasicAuthWebSocketServerProtocol. It can be replaced by a subclass

RaisesTypeError -- if the credentials or check_credentials argument is
wrong.

class websockets. auth. BasicAuthWebSocketServerProtocol[*args, realm=None, check_credentials=None, **kwargs]WebSocket server protocol that enforces HTTP Basic Auth.

realm. str = ''Scope of protection.

If provided, it should contain only ASCII characters because the encoding of non-ASCII characters is undefined

username. Optional[str] = NoneUsername of the authenticated user

await check_credentials[username, password]Check whether credentials are authorized

This coroutine may be overridden in a subclass, for example to authenticate against a database or an external service

Parameters

  • username [str] -- HTTP Basic Auth username
  • password [str] -- HTTP Basic Auth password

ReturnsTrue if the handshake should continue; False if it should fail with a HTTP 401 error. Trả lại bảng đánh máy

Sans-I/O

class websockets. server. ServerConnection[origins=None, extensions=None, subprotocols=None, state=State. CONNECTING, max_size=2**20, logger=None]Sans-I/O implementation of a WebSocket server connection

Parameters

  • origins [Optional[Sequence[Optional[Origin]]]] -- acceptable values of the Origin header; include None in the list if the lack of an origin is acceptable. This is useful for defending against Cross-Site WebSocket Hijacking attacks
  • extensions [List[Extension]] -- list of supported extensions, in order in which they should be tried
  • subprotocols [Optional[Sequence[Subprotocol]]] -- list of supported subprotocols, in order of decreasing preference
  • state [State] -- initial state of the WebSocket connection
  • max_size [Optional[int]] -- maximum size of incoming messages in bytes; None to disable the limit
  • logger [Union[Logger, LoggerAdapter]] -- logger for this connection; defaults to logging. getLogger["websockets. client"]; see the logging guide for details

receive_data[data]Receive data from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You should call events_received[] and process resulting events

RaisesEOFError -- if receive_eof[] was called earlier

receive_eof[]Receive the end of the data stream from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You aren't expected to call events_received[]; it won't return any new events

RaisesEOFError -- if receive_eof[] was called earlier

accept[request]Create a handshake response to accept the connection

If the connection cannot be established, the handshake response actually rejects the handshake

You must send the handshake response with send_response[]

You can modify it before sending it, for example to add HTTP headers

Parametersrequest [Request] -- WebSocket handshake request event received from the client. ReturnsWebSocket handshake response event to send to the client. Return typeResponse

reject[status, text]Create a handshake response to reject the connection

A short plain text response is the best fallback when failing to establish a WebSocket connection

You must send the handshake response with send_response[]

You can modify it before sending it, for example to alter HTTP headers

Parameters

  • status [HTTPStatus] -- HTTP status code
  • text [str] -- HTTP response body; will be encoded to UTF-8

ReturnsWebSocket handshake response event to send to the client. Return typeResponse

send_response[response]Send a handshake response to the client

Parametersresponse [Response] -- WebSocket handshake response event to send

send_continuation[data, fin]Send a Continuation frame

Parameters

  • data [bytes] -- payload containing the same kind of data as the initial frame
  • fin [bool] -- FIN bit; set it to True if this is the last frame of a fragmented message and to False otherwise

RaisesProtocolError -- if a fragmented message isn't in progress

send_text[data, fin=True]Send a Text frame

Parameters

  • data [bytes] -- payload containing text encoded with UTF-8
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_binary[data, fin=True]Send a Binary frame

Parameters

  • data [bytes] -- payload containing arbitrary binary data
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_close[code=None, reason='']Send a Close frame

Parameters

  • code [Optional[int]] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- if a fragmented message is being sent, if the code
isn't valid, or if a reason is provided without a code

send_ping[data]Send a Ping frame

Parametersdata [bytes] -- payload containing arbitrary binary data

send_pong[data]Send a Pong frame

Parametersdata [bytes] -- payload containing arbitrary binary data

fail[code, reason='']Fail the WebSocket connection

Parameters

  • code [int] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- nếu mã không hợp lệ

events_received[]Fetch events generated from data received from the network

Call this method immediately after any of the receive_*[] methods

Process resulting events, likely by passing them to the application

ReturnsEvents read from the connection. Return typeList[Event]

data_to_send[]Obtain data to send to the network

Call this method immediately after any of the receive_*[], send_*[], or fail[] methods

Write resulting data to the connection

The empty bytestring SEND_EOF signals the end of the data stream. When you receive it, half-close the TCP connection

ReturnsData to write to the connection. Return typeList[bytes]

close_expected[]Tell if the TCP connection is expected to close soon

Call this method immediately after any of the receive_*[] or fail[] methods

If it returns True, schedule closing the TCP connection after a short timeout if the other side hasn't already closed it

ReturnsWhether the TCP connection is expected to close soon. Return typebool

id. uuid. UUIDUnique identifier of the connection. Useful in logs

logger. LoggerLikeLogger for this connection

property state. StateWebSocket connection state

Defined in 4. 1, 4. 2, 7. 1. 3, and 7. 1. 4 of RFC 6455

handshake_exc. Optional[Exception]Exception to raise if the opening handshake failed

None if the opening handshake succeeded

property close_code. Tùy chọn[int]Mã đóng WebSocket

None if the connection isn't closed yet

bất động sản close_reason. Tùy chọn[str]lý do đóng WebSocket

None if the connection isn't closed yet

thuộc tính close_exc. ConnectionClosedException tăng khi cố gắng tương tác với một kết nối đã đóng

Don't raise this exception while the connection state is CLOSING; wait until it's CLOSED

Thật vậy, ngoại lệ bao gồm mã đóng và lý do, chỉ được biết khi kết nối bị đóng

RaisesAssertionError - nếu kết nối chưa được đóng

Client

asyncio

Opening a connection

đang chờ ổ cắm web. khách hàng. kết nối [uri, *, create_protocol=Không, logger=Không, nén='deflate', origin=Không, phần mở rộng=Không, giao thức con=Không, extra_headers=Không, user_agent_header='Python/x. y. ổ cắm web z/X. Y', open_timeout=10, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16, **kwds

Đang chờ kết nối[] mang lại một WebSocketClientProtocol mà sau đó có thể được sử dụng để gửi và nhận tin nhắn

connect[] có thể được sử dụng làm trình quản lý bối cảnh không đồng bộ

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
40

Kết nối được đóng tự động khi thoát khỏi ngữ cảnh

connect[] có thể được sử dụng như một trình lặp không đồng bộ vô hạn để tự động kết nối lại khi có lỗi

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
23

Kết nối được đóng tự động sau mỗi lần lặp lại vòng lặp

Nếu xảy ra lỗi trong khi thiết lập kết nối, connect[] sẽ thử lại theo cấp số nhân. Độ trễ lùi lại bắt đầu sau ba giây và tăng lên đến một phút

If an error occurs in the body of the loop, you can handle the exception and connect[] will reconnect with the next iteration; or you can let the exception bubble up and break out of the loop. Điều này cho phép bạn quyết định lỗi nào kích hoạt kết nối lại và lỗi nào nghiêm trọng

Parameters

  • uri [str] -- URI của máy chủ WebSocket
  • create_protocol [Tùy chọn[Có thể gọi [[Bất kỳ], WebSocketClientProtocol]]] -- nhà máy cho asyncio. Giao thức quản lý kết nối;
  • logger [Tùy chọn[LoggerLike]] -- logger cho kết nối này; . getLogger["ổ cắm web. client"]; xem hướng dẫn ghi nhật ký để biết chi tiết
  • compression [Optional[str]] -- shortcut that enables the "permessage-deflate" extension by default; may be set to None to disable compression; see the compression guide for details
  • gốc [Tùy chọn [Xuất xứ]] -- giá trị của tiêu đề Gốc. Điều này hữu ích khi kết nối với máy chủ xác thực tiêu đề Gốc để bảo vệ chống lại các cuộc tấn công Chiếm quyền điều khiển WebSocket trên nhiều trang web
  • tiện ích mở rộng [Tùy chọn[Sequence[ClientExtensionFactory]]] -- danh sách các tiện ích mở rộng được hỗ trợ, theo thứ tự chúng nên được dùng thử
  • subprotocols [Optional[Sequence[Subprotocol]]] -- list of supported subprotocols, in order of decreasing preference
  • extra_headers [Tùy chọn[HeadersLike]] -- tiêu đề HTTP tùy ý để thêm vào yêu cầu
  • user_agent_header [Tùy chọn[str]] -- giá trị của tiêu đề yêu cầu Tác nhân người dùng; . y. ổ cắm web z/X. Y"; Không xóa tiêu đề
  • open_timeout [Tùy chọn[float]] -- thời gian chờ để mở kết nối tính bằng giây;

See WebSocketCommonProtocol for the documentation of ping_interval, ping_timeout, close_timeout, max_size, max_queue, read_limit, and write_limit

Bất kỳ đối số từ khóa nào khác được chuyển qua phương thức create_connection[] của vòng lặp sự kiện

For example

  • Bạn có thể đặt ssl thành SSLContext để thực thi cài đặt TLS. When connecting to a wss. // URI, nếu ssl không được cung cấp, ngữ cảnh TLS được tạo bằng create_default_context[]
  • Bạn có thể đặt Máy chủ và cổng để kết nối với một Máy chủ và cổng khác với các máy chủ và cổng được tìm thấy trong uri. Điều này chỉ thay đổi đích của kết nối TCP. Tên máy chủ từ uri vẫn được sử dụng trong bắt tay TLS cho các kết nối an toàn và trong tiêu đề Máy chủ

Trả về kết nối WebSocket. Kiểu trả vềWebSocketClientProtocolRaises

  • InvalidURI -- nếu uri không phải là một WebSocket URI hợp lệ
  • InvalidHandshake -- nếu bắt tay mở không thành công
  • TimeoutError -- nếu quá trình bắt tay mở hết thời gian chờ

đang chờ ổ cắm web. khách hàng. unix_connect[đường dẫn, uri='ws. //localhost/', *, create_protocol=Không, logger=Không, nén='deflate', origin=Không, tiện ích mở rộng=Không, giao thức con=Không, extra_headers=Không, user_agent_header='Python/x. y. ổ cắm web z/X. Y', open_timeout=10, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16, **kwds]Similar to connect[], but for connecting to a Unix socket

Chức năng này được xây dựng dựa trên phương thức create_unix_connection[] của vòng lặp sự kiện

It is only available on Unix

Nó chủ yếu hữu ích cho việc gỡ lỗi các máy chủ đang nghe trên các ổ cắm Unix

Parameters

  • đường dẫn [Tùy chọn [str]] - đường dẫn hệ thống tệp đến ổ cắm Unix
  • uri [str] -- URI của máy chủ WebSocket;

Using a connection

lớp websocket. khách hàng. WebSocketClientProtocol[*, logger=None, origin=None, extension=None, subprotocols=None, extra_headers=None, user_agent_header='Python/x. y. ổ cắm web z/X. Y', ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16]Kết nối máy khách WebSocket

WebSocketClientProtocol cung cấp các coroutine recv[] và send[] để nhận và gửi tin nhắn

Nó hỗ trợ phép lặp không đồng bộ để nhận tin nhắn đến

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
37

The iterator exits normally when the connection is closed with close code 1000 [OK] or 1001 [going away]. It raises a ConnectionClosedError when the connection is closed with any other code

Xem connect[] để biết tài liệu về logger, Origin, tiện ích mở rộng, giao thức con, extra_headers và user_agent_header

See WebSocketCommonProtocol for the documentation of ping_interval, ping_timeout, close_timeout, max_size, max_queue, read_limit, and write_limit


await recv[]Receive the next message

When the connection is closed, recv[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure. This is how you detect the end of the message stream

Canceling recv[] is safe. There's no risk of losing the next message. The next invocation of recv[] will return it

This makes it possible to enforce a timeout by wrapping recv[] in wait_for[]

ReturnsA string [str] for a Text frame. A bytestring [bytes] for a Binary frame. Return typeDataRaises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if two coroutines call recv[] concurrently

await send[message]Send a message

A string [str] is sent as a Text frame. A bytestring or bytes-like object [bytes, bytearray, or memoryview] is sent as a Binary frame

send[] also accepts an iterable or an asynchronous iterable of strings, bytestrings, or bytes-like objects to enable fragmentation. Each item is treated as a message fragment and sent in its own frame. All items must be of the same type, or else send[] will raise a TypeError and the connection will be closed

send[] rejects dict-like objects because this is often an error. [If you want to send the keys of a dict-like object as fragments, call its keys[] method and pass the result to send[]. ]

Hủy gửi[] không được khuyến khích. Instead, you should close the connection with close[]. Indeed, there are only two situations where send[] may yield control to the event loop and then get canceled; in both cases, close[] has the same effect and is more clear

1. The write buffer is full. If you don't want to wait until enough data is sent, your only alternative is to close the connection. close[] will likely time out then abort the TCP connection. 2. message is an asynchronous iterator that yields control. Stopping in the middle of a fragmented message will cause a protocol error and the connection will be closed

When the connection is closed, send[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure

Parametersmessage [Union[Data, Iterable[Data], AsyncIterable[Data]] -- message to send. Raises

  • ConnectionClosed -- when the connection is closed
  • TypeError -- if message doesn't have a supported type

await close[code=1000, reason='']Perform the closing handshake

close[] waits for the other end to complete the handshake and for the TCP connection to terminate. As a consequence, there's no need to await wait_closed[] after close[]

close[] is idempotent. it doesn't do anything once the connection is closed

Wrapping close[] in create_task[] is safe, given that errors during connection termination aren't particularly useful

Canceling close[] is discouraged. If it takes too long, you can set a shorter close_timeout. If you don't want to wait, let the Python process exit, then the OS will take care of closing the TCP connection

Parameters

  • code [int] -- WebSocket close code
  • reason [str] -- WebSocket close reason

await wait_closed[]Wait until the connection is closed

This coroutine is identical to the closed attribute, except it can be awaited

This can make it easier to detect connection termination, regardless of its cause, in tasks that interact with the WebSocket connection


await ping[data=None]Send a Ping

A ping may serve as a keepalive, as a check that the remote endpoint received all messages up to this point, or to measure latency

Canceling ping[] is discouraged. If ping[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Canceling the Future returned by ping[] has no effect

Parametersdata [Optional[Data]] -- payload of the ping; a string will be encoded to UTF-8; or None to generate a payload containing four random bytes. ReturnsA future that will be completed when the corresponding pong is received. You can ignore it if you don't intend to wait. The result of the future is the latency of the connection in seconds

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
38

Return typeFuture[float]Raises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if another ping was sent with the same data and
    the corresponding pong wasn't received yet.

await pong[data=b'']Send a Pong

An unsolicited pong may serve as a unidirectional heartbeat

Canceling pong[] is discouraged. If pong[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Parametersdata [Data] -- payload of the pong; a string will be encoded to UTF-8. RaisesConnectionClosed -- when the connection is closed

WebSocket connection objects also provide these attributes

id. uuid. UUIDUnique identifier of the connection. Useful in logs

logger. LoggerLikeLogger for this connection

property local_address. AnyLocal address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getsockname[]

None if the TCP connection isn't established yet

property remote_address. AnyRemote address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getpeername[]

None if the TCP connection isn't established yet

property open. boolTrue when the connection is open; False otherwise

This attribute may be used to detect disconnections. However, this approach is discouraged per the EAFP principle. Instead, you should handle ConnectionClosed exceptions

property closed. boolTrue when the connection is closed; False otherwise

Be aware that both open and closed are False during the opening and closing sequences

latency. floatLatency of the connection, in seconds

This value is updated after sending a ping frame and receiving a matching pong frame. Trước ping đầu tiên, độ trễ là 0

By default, websockets enables a keepalive mechanism that sends ping frames automatically at regular intervals. You can also send ping frames and measure latency with ping[]

The following attributes are available after the opening handshake, once the WebSocket connection is open

path. strPath of the opening handshake request

request_headers. HeadersOpening handshake request headers

response_headers. HeadersOpening handshake response headers

subprotocol. Optional[Subprotocol]Subprotocol, if one was negotiated

The following attributes are available after the closing handshake, once the WebSocket connection is closed

property close_code. Optional[int]WebSocket close code, defined in section 7. 1. 5 of RFC 6455

None if the connection isn't closed yet

property close_reason. Optional[str]WebSocket close reason, defined in section 7. 1. 6 of RFC 6455

None if the connection isn't closed yet

Sans-I/O

lớp websocket. khách hàng. ClientConnection[wsuri, origin=None, extension=None, subprotocols=None, state=State. KẾT NỐI, max_size=2**20, logger=None]Triển khai Sans-I/O của kết nối máy khách WebSocket

Parameters

  • wsuri [WebSocketURI] -- URI của máy chủ WebSocket, được phân tích cú pháp bằng parse_uri[]
  • gốc [Tùy chọn [Xuất xứ]] -- giá trị của tiêu đề Gốc. Điều này hữu ích khi kết nối với máy chủ xác thực tiêu đề Gốc để bảo vệ chống lại các cuộc tấn công Chiếm quyền điều khiển WebSocket trên nhiều trang web
  • tiện ích mở rộng [Tùy chọn[Sequence[ClientExtensionFactory]]] -- danh sách các tiện ích mở rộng được hỗ trợ, theo thứ tự chúng nên được dùng thử
  • subprotocols [Optional[Sequence[Subprotocol]]] -- list of supported subprotocols, in order of decreasing preference
  • state [State] -- initial state of the WebSocket connection
  • max_size [Optional[int]] -- maximum size of incoming messages in bytes; None to disable the limit
  • logger [Tùy chọn[LoggerLike]] -- logger cho kết nối này; . getLogger["ổ cắm web. client"]; xem hướng dẫn ghi nhật ký để biết chi tiết

receive_data[data]Receive data from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You should call events_received[] and process resulting events

RaisesEOFError -- if receive_eof[] was called earlier

receive_eof[]Receive the end of the data stream from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You aren't expected to call events_received[]; it won't return any new events

RaisesEOFError -- if receive_eof[] was called earlier

connect[] Tạo yêu cầu bắt tay để mở kết nối

Bạn phải gửi yêu cầu bắt tay với send_request[]

You can modify it before sending it, for example to add HTTP headers

Trả về sự kiện yêu cầu bắt tay WebSocket để gửi đến máy chủ. Return typeRequest

send_request[request]Gửi yêu cầu bắt tay tới máy chủ

Yêu cầu tham số [Yêu cầu] -- Sự kiện yêu cầu bắt tay WebSocket

send_continuation[data, fin]Send a Continuation frame

Parameters

  • data [bytes] -- payload containing the same kind of data as the initial frame
  • fin [bool] -- FIN bit; set it to True if this is the last frame of a fragmented message and to False otherwise

RaisesProtocolError -- if a fragmented message isn't in progress

send_text[data, fin=True]Send a Text frame

Parameters

  • data [bytes] -- payload containing text encoded with UTF-8
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_binary[data, fin=True]Send a Binary frame

Parameters

  • data [bytes] -- payload containing arbitrary binary data
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_close[code=None, reason='']Send a Close frame

Parameters

  • code [Optional[int]] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- if a fragmented message is being sent, if the code
isn't valid, or if a reason is provided without a code

send_ping[data]Send a Ping frame

Parametersdata [bytes] -- payload containing arbitrary binary data

send_pong[data]Send a Pong frame

Parametersdata [bytes] -- payload containing arbitrary binary data

fail[code, reason='']Fail the WebSocket connection

Parameters

  • code [int] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- nếu mã không hợp lệ

events_received[]Fetch events generated from data received from the network

Call this method immediately after any of the receive_*[] methods

Process resulting events, likely by passing them to the application

ReturnsEvents read from the connection. Return typeList[Event]

data_to_send[]Obtain data to send to the network

Call this method immediately after any of the receive_*[], send_*[], or fail[] methods

Write resulting data to the connection

The empty bytestring SEND_EOF signals the end of the data stream. When you receive it, half-close the TCP connection

ReturnsData to write to the connection. Return typeList[bytes]

close_expected[]Tell if the TCP connection is expected to close soon

Call this method immediately after any of the receive_*[] or fail[] methods

If it returns True, schedule closing the TCP connection after a short timeout if the other side hasn't already closed it

ReturnsWhether the TCP connection is expected to close soon. Return typebool

id. uuid. UUIDUnique identifier of the connection. Useful in logs

logger. LoggerLikeLogger for this connection

property state. StateWebSocket connection state

Defined in 4. 1, 4. 2, 7. 1. 3, and 7. 1. 4 of RFC 6455

handshake_exc. Optional[Exception]Exception to raise if the opening handshake failed

None if the opening handshake succeeded

property close_code. Tùy chọn[int]Mã đóng WebSocket

None if the connection isn't closed yet

bất động sản close_reason. Tùy chọn[str]lý do đóng WebSocket

None if the connection isn't closed yet

thuộc tính close_exc. ConnectionClosedException tăng khi cố gắng tương tác với một kết nối đã đóng

Don't raise this exception while the connection state is CLOSING; wait until it's CLOSED

Thật vậy, ngoại lệ bao gồm mã đóng và lý do, chỉ được biết khi kết nối bị đóng

RaisesAssertionError - nếu kết nối chưa được đóng

Both sides

asyncio

lớp websocket. di sản. giao thức. WebSocketCommonProtocol[*, logger=None, ping_interval=20, ping_timeout=20, close_timeout=10, max_size=2**20, max_queue=2**5, read_limit=2**16, write_limit=2**16]Kết nối WebSocket

WebSocketCommonProtocol cung cấp API được chia sẻ giữa máy chủ và máy khách WebSocket. Bạn không nên sử dụng trực tiếp. Thay vào đó, hãy sử dụng WebSocketClientProtocol hoặc WebSocketServerProtocol

Tài liệu này tập trung vào các chi tiết cấp thấp không có trong tài liệu của WebSocketClientProtocol và WebSocketServerProtocol vì mục đích đơn giản

Khi kết nối được mở, khung Ping sẽ được gửi sau mỗi ping_interval giây. Điều này phục vụ như là một keepalive. Nó giúp duy trì kết nối mở, đặc biệt là khi có các proxy có thời gian chờ ngắn trên các kết nối không hoạt động. Đặt ping_interval thành Không để tắt hành vi này

Nếu không nhận được khung Pong tương ứng trong vòng ping_timeout giây, kết nối được coi là không sử dụng được và bị đóng bằng mã 1011. Điều này đảm bảo rằng điểm cuối từ xa vẫn đáp ứng. Đặt ping_timeout thành Không để tắt hành vi này

Tham số close_timeout xác định thời gian chờ tối đa để hoàn thành quá trình bắt tay đóng và kết thúc kết nối TCP. Vì các lý do cũ, close[] hoàn thành trong tối đa 5 * close_timeout giây cho máy khách và 4 * close_timeout cho máy chủ

See the discussion of timeouts for details

close_timeout cần phải là một tham số của giao thức vì websockets thường gọi close[] ngầm khi thoát

  • về phía máy khách, khi connect[] được sử dụng làm trình quản lý bối cảnh;
  • về phía máy chủ, khi trình xử lý kết nối chấm dứt;

Để áp dụng thời gian chờ cho bất kỳ API nào khác, hãy bọc nó trong wait_for[]

Tham số max_size thực thi kích thước tối đa cho các tin nhắn đến theo byte. Giá trị mặc định là 1 MiB. Nếu nhận được thông báo lớn hơn, recv[] sẽ tăng ConnectionClosedError và kết nối sẽ bị đóng với mã 1009

Tham số max_queue đặt độ dài tối đa của hàng đợi chứa thư đến. Giá trị mặc định là 32. Tin nhắn được thêm vào hàng đợi trong bộ nhớ khi chúng được nhận; . Để tránh tiêu thụ bộ nhớ quá mức khi nhận tin nhắn nhanh hơn tốc độ xử lý, hàng đợi phải được giới hạn. Nếu hàng đợi đầy, giao thức sẽ dừng xử lý dữ liệu đến cho đến khi recv[] được gọi. Trong tình huống này, nhiều bộ đệm nhận khác nhau [ít nhất là trong asyncio và trong HĐH] sẽ đầy, sau đó cửa sổ nhận TCP sẽ co lại, làm chậm quá trình truyền để tránh mất gói

Vì Python có thể sử dụng tối đa 4 byte bộ nhớ để biểu thị một ký tự, nên mỗi kết nối có thể sử dụng tối đa 4 * max_size * max_queue byte bộ nhớ để lưu trữ thư đến. Theo mặc định, đây là 128 MiB. Bạn có thể muốn hạ thấp giới hạn, tùy thuộc vào yêu cầu của ứng dụng của bạn

Đối số read_limit đặt giới hạn nước cao của bộ đệm cho các byte đến. Giới hạn nước thấp bằng một nửa giới hạn nước cao. Giá trị mặc định là 64 KiB, bằng một nửa giá trị mặc định của asyncio [dựa trên việc triển khai StreamReader hiện tại]

Đối số write_limit đặt giới hạn nước cao của bộ đệm cho các byte gửi đi. Giới hạn nước thấp bằng một phần tư giới hạn nước cao. The default value is 64 KiB, equal to asyncio's default [based on the current implementation of FlowControlMixin]

Xem phần thảo luận về sử dụng bộ nhớ để biết chi tiết

Parameters

  • logger [Tùy chọn[LoggerLike]] -- logger cho kết nối này; . getLogger["ổ cắm web. giao thức"]; xem hướng dẫn ghi nhật ký để biết chi tiết
  • ping_interval [Tùy chọn[float]] -- độ trễ giữa các lần ping cố định tính bằng giây;
  • ping_timeout [Tùy chọn[float]] -- thời gian chờ cho các ping cố định tính bằng giây;
  • close_timeout [Tùy chọn[float]] -- thời gian chờ để đóng kết nối sau vài giây;
  • max_size [Optional[int]] -- maximum size of incoming messages in bytes; None to disable the limit
  • max_queue [Tùy chọn[int]] -- số lượng tin nhắn đến tối đa trong bộ đệm nhận;
  • read_limit [int] - dấu nước cao của bộ đệm đọc theo byte
  • write_limit [int] - dấu nước cao của bộ đệm ghi theo byte

await recv[]Receive the next message

When the connection is closed, recv[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure. This is how you detect the end of the message stream

Canceling recv[] is safe. There's no risk of losing the next message. The next invocation of recv[] will return it

This makes it possible to enforce a timeout by wrapping recv[] in wait_for[]

ReturnsA string [str] for a Text frame. A bytestring [bytes] for a Binary frame. Return typeDataRaises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if two coroutines call recv[] concurrently

await send[message]Send a message

A string [str] is sent as a Text frame. A bytestring or bytes-like object [bytes, bytearray, or memoryview] is sent as a Binary frame

send[] also accepts an iterable or an asynchronous iterable of strings, bytestrings, or bytes-like objects to enable fragmentation. Each item is treated as a message fragment and sent in its own frame. All items must be of the same type, or else send[] will raise a TypeError and the connection will be closed

send[] rejects dict-like objects because this is often an error. [If you want to send the keys of a dict-like object as fragments, call its keys[] method and pass the result to send[]. ]

Hủy gửi[] không được khuyến khích. Instead, you should close the connection with close[]. Indeed, there are only two situations where send[] may yield control to the event loop and then get canceled; in both cases, close[] has the same effect and is more clear

1. The write buffer is full. If you don't want to wait until enough data is sent, your only alternative is to close the connection. close[] will likely time out then abort the TCP connection. 2. message is an asynchronous iterator that yields control. Stopping in the middle of a fragmented message will cause a protocol error and the connection will be closed

When the connection is closed, send[] raises ConnectionClosed. Specifically, it raises ConnectionClosedOK after a normal connection closure and ConnectionClosedError after a protocol error or a network failure

Parametersmessage [Union[Data, Iterable[Data], AsyncIterable[Data]] -- message to send. Raises

  • ConnectionClosed -- when the connection is closed
  • TypeError -- if message doesn't have a supported type

await close[code=1000, reason='']Perform the closing handshake

close[] waits for the other end to complete the handshake and for the TCP connection to terminate. As a consequence, there's no need to await wait_closed[] after close[]

close[] is idempotent. it doesn't do anything once the connection is closed

Wrapping close[] in create_task[] is safe, given that errors during connection termination aren't particularly useful

Canceling close[] is discouraged. If it takes too long, you can set a shorter close_timeout. If you don't want to wait, let the Python process exit, then the OS will take care of closing the TCP connection

Parameters

  • code [int] -- WebSocket close code
  • reason [str] -- WebSocket close reason

await wait_closed[]Wait until the connection is closed

This coroutine is identical to the closed attribute, except it can be awaited

This can make it easier to detect connection termination, regardless of its cause, in tasks that interact with the WebSocket connection


await ping[data=None]Send a Ping

A ping may serve as a keepalive, as a check that the remote endpoint received all messages up to this point, or to measure latency

Canceling ping[] is discouraged. If ping[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Canceling the Future returned by ping[] has no effect

Parametersdata [Optional[Data]] -- payload of the ping; a string will be encoded to UTF-8; or None to generate a payload containing four random bytes. ReturnsA future that will be completed when the corresponding pong is received. You can ignore it if you don't intend to wait. The result of the future is the latency of the connection in seconds

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
38

Return typeFuture[float]Raises

  • ConnectionClosed -- when the connection is closed
  • RuntimeError -- if another ping was sent with the same data and
    the corresponding pong wasn't received yet.

await pong[data=b'']Send a Pong

An unsolicited pong may serve as a unidirectional heartbeat

Canceling pong[] is discouraged. If pong[] doesn't return immediately, it means the write buffer is full. If you don't want to wait, you should close the connection

Parametersdata [Data] -- payload of the pong; a string will be encoded to UTF-8. RaisesConnectionClosed -- when the connection is closed

WebSocket connection objects also provide these attributes

Tôi. UUIDSố nhận dạng duy nhất của kết nối. Hữu ích trong nhật ký

tiều phu. Union[Logger, LoggerAdapter]Logger cho kết nối này

property local_address. AnyLocal address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getsockname[]

None if the TCP connection isn't established yet

property remote_address. AnyRemote address of the connection

For IPv4 connections, this is a [host, port] tuple

The format of the address depends on the address family; see getpeername[]

None if the TCP connection isn't established yet

property open. boolTrue when the connection is open; False otherwise

This attribute may be used to detect disconnections. However, this approach is discouraged per the EAFP principle. Instead, you should handle ConnectionClosed exceptions

property closed. boolTrue when the connection is closed; False otherwise

Be aware that both open and closed are False during the opening and closing sequences

latency. floatLatency of the connection, in seconds

This value is updated after sending a ping frame and receiving a matching pong frame. Trước ping đầu tiên, độ trễ là 0

By default, websockets enables a keepalive mechanism that sends ping frames automatically at regular intervals. You can also send ping frames and measure latency with ping[]

The following attributes are available after the opening handshake, once the WebSocket connection is open

path. strPath of the opening handshake request

request_headers. HeadersOpening handshake request headers

response_headers. HeadersOpening handshake response headers

subprotocol. Optional[Subprotocol]Subprotocol, if one was negotiated

The following attributes are available after the closing handshake, once the WebSocket connection is closed

property close_code. Optional[int]WebSocket close code, defined in section 7. 1. 5 of RFC 6455

None if the connection isn't closed yet

property close_reason. Optional[str]WebSocket close reason, defined in section 7. 1. 6 of RFC 6455

None if the connection isn't closed yet

Sans-I/O

lớp websocket. sự liên quan. Kết nối[bên, trạng thái=Trạng thái. MỞ, max_size=2**20, logger=None]Triển khai Sans-I/O của kết nối WebSocket

Parameters

  • side [Side] -- CLIENT or SERVER
  • state [State] -- initial state of the WebSocket connection
  • max_size [Optional[int]] -- maximum size of incoming messages in bytes; None to disable the limit
  • logger [Optional[LoggerLike]] -- logger for this connection; depending on side, defaults to logging. getLogger["websockets. client"] or logging. getLogger["websockets. server"]; see the logging guide for details

receive_data[data]Receive data from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You should call events_received[] and process resulting events

RaisesEOFError -- if receive_eof[] was called earlier

receive_eof[]Receive the end of the data stream from the network

After calling this method

  • You must call data_to_send[] and send this data to the network
  • You aren't expected to call events_received[]; it won't return any new events

RaisesEOFError -- if receive_eof[] was called earlier

send_continuation[data, fin]Send a Continuation frame

Parameters

  • data [bytes] -- payload containing the same kind of data as the initial frame
  • fin [bool] -- FIN bit; set it to True if this is the last frame of a fragmented message and to False otherwise

RaisesProtocolError -- if a fragmented message isn't in progress

send_text[data, fin=True]Send a Text frame

Parameters

  • data [bytes] -- payload containing text encoded with UTF-8
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_binary[data, fin=True]Send a Binary frame

Parameters

  • data [bytes] -- payload containing arbitrary binary data
  • fin [bool] -- FIN bit; set it to False if this is the first frame of a fragmented message

RaisesProtocolError -- if a fragmented message is in progress

send_close[code=None, reason='']Send a Close frame

Parameters

  • code [Optional[int]] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- if a fragmented message is being sent, if the code
isn't valid, or if a reason is provided without a code

send_ping[data]Send a Ping frame

Parametersdata [bytes] -- payload containing arbitrary binary data

send_pong[data]Send a Pong frame

Parametersdata [bytes] -- payload containing arbitrary binary data

fail[code, reason='']Fail the WebSocket connection

Parameters

  • code [int] -- close code
  • reason [str] -- close reason

RaisesProtocolError -- nếu mã không hợp lệ

events_received[]Fetch events generated from data received from the network

Call this method immediately after any of the receive_*[] methods

Process resulting events, likely by passing them to the application

ReturnsEvents read from the connection. Return typeList[Event]

data_to_send[]Obtain data to send to the network

Call this method immediately after any of the receive_*[], send_*[], or fail[] methods

Write resulting data to the connection

The empty bytestring SEND_EOF signals the end of the data stream. When you receive it, half-close the TCP connection

ReturnsData to write to the connection. Return typeList[bytes]

close_expected[]Tell if the TCP connection is expected to close soon

Call this method immediately after any of the receive_*[] or fail[] methods

If it returns True, schedule closing the TCP connection after a short timeout if the other side hasn't already closed it

ReturnsWhether the TCP connection is expected to close soon. Return typebool

Tôi. UUIDSố nhận dạng duy nhất của kết nối. Hữu ích trong nhật ký

tiều phu. Union[Logger, LoggerAdapter]Logger cho kết nối này

property state. StateWebSocket connection state

Defined in 4. 1, 4. 2, 7. 1. 3, and 7. 1. 4 of RFC 6455

property close_code. Tùy chọn[int]Mã đóng WebSocket

None if the connection isn't closed yet

bất động sản close_reason. Tùy chọn[str]lý do đóng WebSocket

None if the connection isn't closed yet

thuộc tính close_exc. ConnectionClosedException tăng khi cố gắng tương tác với một kết nối đã đóng

Don't raise this exception while the connection state is CLOSING; wait until it's CLOSED

Thật vậy, ngoại lệ bao gồm mã đóng và lý do, chỉ được biết khi kết nối bị đóng

RaisesAssertionError - nếu kết nối chưa được đóng

class websockets. connection. Side[value]A WebSocket connection is either a server or a client

SERVER = 0

KHÁCH HÀNG = 1

class websockets. connection. State[value]A WebSocket connection is in one of these four states

KẾT NỐI = 0

MỞ = 1

ĐÓNG = 2

ĐÃ ĐÓNG CỬA = 3

websockets. sự liên quan. SEND_EOF = b''Sentinel báo hiệu rằng kết nối TCP phải được đóng một nửa

tiện ích

Broadcast

websockets. phát sóng [websockets, tin nhắn] Phát một tin nhắn đến một số kết nối WebSocket

A string [str] is sent as a Text frame. A bytestring or bytes-like object [bytes, bytearray, or memoryview] is sent as a Binary frame

broadcast[] pushes the message synchronously to all connections even if their write buffers are overflowing. Không có áp suất ngược

Broadcast[] bỏ qua các kết nối âm thầm không mở để tránh lỗi trên các kết nối đang diễn ra quá trình bắt tay đóng

Nếu bạn phát tin nhắn nhanh hơn tốc độ kết nối có thể xử lý chúng, tin nhắn sẽ chồng chất trong bộ đệm ghi của nó cho đến khi hết thời gian kết nối. Giữ các giá trị thấp cho ping_interval và ping_timeout để tránh sử dụng bộ nhớ quá mức do kết nối chậm khi bạn sử dụng quảng bá []

Không giống như send[], Broadcast[] không hỗ trợ gửi tin nhắn bị phân mảnh. Thật vậy, phân mảnh rất hữu ích để gửi các tin nhắn lớn mà không cần đệm chúng trong bộ nhớ, trong khi quảng bá [] sẽ đệm một bản sao cho mỗi kết nối nhanh nhất có thể

Parameters

  • websockets [Iterable[WebSocketCommonProtocol]] -- Các kết nối WebSocket mà tin nhắn sẽ được gửi tới
  • tin nhắn [Dữ liệu] -- tin nhắn cần gửi

tăng

  • RuntimeError -- nếu kết nối đang bận gửi tin nhắn bị phân mảnh
  • TypeError -- if message doesn't have a supported type

Sự kiện WebSocket

ổ cắm web lớp. khung. Khung[opcode, data, fin=True, rsv1=False, rsv2=False, rsv3=False]Khung WebSocket.

opcodeOpcode.

Loạiwebsockets. khung. mã hóa

dataPayload data

kiểu chữ

bitFINFIN

đánh máy

bit rsv1RSV1

đánh máy

rsv2RSV2 bit

đánh máy

bit rsv3RSV3

đánh máy

Chỉ những lĩnh vực này là cần thiết. Bit MASK, độ dài tải trọng và khóa mặt nạ được xử lý nhanh chóng khi phân tích cú pháp và tuần tự hóa các khung

lớp websocket. khung. Opcode[value]Các giá trị opcode cho các khung WebSocket

TIẾP TỤC = 0

VĂN BẢN = 1

NHỊP NHI = 2

ĐÓNG = 8

PING = 9

PHONG = 10

ổ cắm web lớp. khung. Close[code, reason]Code and reason for WebSocket close frames.

codeĐóng mã.

đánh máy

reasonClose reason

Typestr

HTTP events

ổ cắm web lớp. http11. Yêu cầu[đường dẫn, tiêu đề, _Exception=None]Yêu cầu bắt tay WebSocket.

pathĐường dẫn yêu cầu, bao gồm truy vấn tùy chọn.

Typestr

headersRequest headers

Typewebsockets. cấu trúc dữ liệu. Headers

class websockets. http11. Phản hồi[status_code, reason_phrase, headers, body=None, _Exception=None]Phản hồi bắt tay WebSocket.

status_codeResponse code.

đánh máy

reason_phraseResponse reason

Typestr

tiêu đề tiêu đề phản hồi

Typewebsockets. cấu trúc dữ liệu. Headers

bodyResponse body, if any

TypeOptional[bytes]

lớp websocket. cấu trúc dữ liệu. Headers[*args, **kwargs]Efficient data structure for manipulating HTTP headers

A list of [name, values] is inefficient for lookups

A dict doesn't suffice because header names are case-insensitive and multiple occurrences of headers with the same name are possible

Headers stores HTTP headers in a hybrid data structure to provide efficient insertions and lookups while preserving the original data

Để tính toán nhiều giá trị với ít rắc rối nhất, Tiêu đề tuân theo logic này

Khi nhận được tiêu đề có tiêu đề [tên]

  • nếu không có giá trị, KeyError được nâng lên;
  • if there's exactly one value, it's returned;
  • nếu có nhiều hơn một giá trị, MultipleValuesError sẽ xuất hiện

  • When setting a header with headers[name] = value, the value is appended to the list of values for that header
  • Khi xóa một tiêu đề bằng del headers[name], tất cả các giá trị cho tiêu đề đó sẽ bị xóa [điều này chậm]

Các phương pháp khác để thao tác tiêu đề phù hợp với logic này

As long as no header occurs multiple times, Headers behaves like dict, except keys are lower-cased to provide case-insensitivity

Hai phương thức hỗ trợ thao tác nhiều giá trị một cách rõ ràng

  • get_all[] trả về danh sách tất cả các giá trị cho tiêu đề;
  • raw_items[] returns an iterator of [name, values] pairs


get_all[key]Trả về danh sách [có thể trống] của tất cả các giá trị cho tiêu đề

Parameterskey [str] -- header name

raw_items[]Trả về một trình vòng lặp của tất cả các giá trị dưới dạng cặp [tên, giá trị]

exception websockets. datastructures. MultipleValuesErrorException xuất hiện khi Tiêu đề có nhiều giá trị cho một khóa

URI

websockets. uri. parse_uri[uri]Parse and validate a WebSocket URI

Tham sốuri [str] -- WebSocket URI. ReturnsParsed WebSocket URI. Return typeWebSocketURIRaisesInvalidURI -- if uri isn't a valid WebSocket URI

class websockets. nước tiểu. WebSocketURI[secure, host, port, path, query, username=None, password=None]WebSocket URI.

secureTrue for a wss URI, False for a ws URI.

đánh máy

hostNormalized to lower case

Typestr

portAlways đặt ngay cả khi đó là mặc định

đánh máy

pathMay be empty

Typestr

queryMay be empty if the URI doesn't include a query component

Typestr

usernameAvailable when the URI contains User Information

TypeOptional[str]

passwordCó sẵn khi URI chứa Thông tin người dùng

TypeOptional[str]

ngoại lệ

websockets. ngoại lệ xác định hệ thống phân cấp ngoại lệ sau

WebSocketException

Kêt nôi bị đong

  • ConnectionClosedError
  • ConnectionClosedOK

InvalidHandshake

  • SecurityError
  • InvalidMessage

InvalidHeader

  • InvalidHeaderFormat
  • InvalidHeaderValue
  • InvalidOrigin
  • InvalidUpgrade

  • InvalidStatus
  • Mã trạng thái không hợp lệ [cũ]

NegotiationError

  • Thông số trùng lặp
  • Tên tham số không hợp lệ
  • InvalidParameterValue

  • AbortHandshake
  • RedirectHandshake

  • InvalidState
  • InvalidURI
  • Tải Trọng Quá Lớn
  • ProtocolError

ổ cắm web ngoại lệ. exceptions. AbortHandshake[status, headers, body=b'']Raised to abort the handshake on purpose and return a HTTP response

Ngoại lệ này là một chi tiết thực hiện

The public API is process_request[]


statusHTTP status code

TypeHTTPStatus

tiêu đềtiêu đề phản hồi HTTP

Loại tiêu đề

bodyHTTP response body

kiểu chữ

exception websockets. exceptions. ConnectionClosed[rcvd, đã gửi, rcvd_then_sent=None]Xảy ra khi cố gắng tương tác với một kết nối đã đóng.

rcvdif đã nhận được khung đóng, mã và lý do của khung đó có sẵn trong rcvd. mã và rcvd. reason.

LoạiTùy chọn[Đóng]

gửi nếu một khung gần được gửi, mã và lý do của nó có sẵn trong khung đã gửi. code and sent. lý do

LoạiTùy chọn[Đóng]

rcvd_then_sentif close frames were received and sent, this attribute tells in which order this happened, from the perspective of this side of the connection

TypeOptional[bool]

exception websockets. ngoại lệ. ConnectionClosedError[rcvd, đã gửi, rcvd_then_sent=None]Giống như ConnectionClosed, khi kết nối bị lỗi

A close code other than 1000 [OK] or 1001 [going away] was received or sent, or the closing handshake didn't complete properly


exception websockets. exceptions. ConnectionClosedOK[rcvd, đã gửi, rcvd_then_sent=None]Giống như ConnectionClosed, khi kết nối bị ngắt đúng cách

A close code 1000 [OK] or 1001 [going away] was received and sent


ổ cắm web ngoại lệ. exceptions. DuplicateParameter[name]Raised when a parameter name is repeated in an extension header

exception websockets. ngoại lệ. InvalidHandshakeRaised during the handshake when the WebSocket connection fails

exception websockets. ngoại lệ. InvalidHeader[name, value=None]Xảy ra khi tiêu đề HTTP không có định dạng hoặc giá trị hợp lệ

exception websockets. exceptions. InvalidHeaderFormat[name, error, header, pos]Raised when a HTTP header cannot be parsed

The format of the header doesn't match the grammar for that header


exception websockets. ngoại lệ. InvalidHeaderValue[name, value=None]Raised when a HTTP header has a wrong value

The format of the header is correct but a value isn't acceptable


exception websockets. ngoại lệ. InvalidMessageRaised when a handshake request or response is malformed

ổ cắm web ngoại lệ. ngoại lệ. InvalidOrigin[origin]Raised when the Origin header in a request isn't allowed

exception websockets. exceptions. InvalidParameterName[name]Raised when a parameter name in an extension header is invalid

exception websockets. exceptions. UnlimitedParameterValue[name, value]Tăng lên khi giá trị tham số trong tiêu đề tiện ích mở rộng không hợp lệ

ổ cắm web ngoại lệ. exceptions. InvalidStateRaised when an operation is forbidden in the current state

Ngoại lệ này là một chi tiết thực hiện

Nó không bao giờ nên được nâng lên trong trường hợp bình thường

exception websockets. ngoại lệ. Trạng thái không hợp lệ[phản hồi]Xảy ra khi phản hồi bắt tay từ chối nâng cấp WebSocket

ổ cắm web ngoại lệ. ngoại lệ. InvalidStatusCode[status_code, headers]Xảy ra khi mã trạng thái phản hồi bắt tay không hợp lệ

exception websockets. ngoại lệ. InvalidURI[uri, msg]Raised when connecting to an URI that isn't a valid WebSocket URI

ổ cắm web ngoại lệ. ngoại lệ. InvalidUpgrade[name, value=None]Tăng lên khi tiêu đề Nâng cấp hoặc Kết nối không chính xác

exception websockets. ngoại lệ. NegotiationErrorRaised khi thương lượng tiện ích mở rộng không thành công

exception websockets. exceptions. PayloadTooBigRaised khi nhận khung có tải trọng vượt quá kích thước tối đa

ổ cắm web ngoại lệ. exceptions. ProtocolErrorRaised khi một khung phá vỡ giao thức

ổ cắm web ngoại lệ. ngoại lệ. RedirectHandshake[uri]Xảy ra khi bắt tay được chuyển hướng

Ngoại lệ này là một chi tiết thực hiện


ổ cắm web ngoại lệ. ngoại lệ. SecurityErrorRaised when a handshake request or response breaks a security rule

Giới hạn bảo mật được mã hóa cứng

ổ cắm web ngoại lệ. ngoại lệ. Lớp WebSocketExceptionBase cho tất cả các ngoại lệ được xác định bởi websockets

websockets. ngoại lệ. WebSocketProtocolErroralias of ProtocolError

các loại

websockets. đánh máy. DataTypes supported in a WebSocket message. str cho khung Văn bản, byte cho nhị phân

alias of Union[str, bytes]

websockets. đánh máy. LoggerLikeTypes được chấp nhận khi Logger được mong đợi

bí danh của Union[Logger, LoggerAdapter]

websockets. typing. OriginValue của tiêu đề Origin

alias of str

websockets. đánh máy. Giao thức conSubprotocol trong tiêu đề Sec-WebSocket-Protocol

alias of str

websockets. typing. ExtensionNameName of a WebSocket extension

alias of str

websockets. đánh máy. ExtensionParameterParameter của tiện ích mở rộng WebSocket

bí danh của Tuple[str, Tùy chọn[str]]

websockets. sự liên quan. EventEvents that events_received[] may return

alias of Union[Request, Response, Frame]

websockets. datastructures. HeadersLikeTypes được chấp nhận khi Headers được mong đợi

Ngoài chính Tiêu đề, điều này bao gồm các loại giống như chính tả trong đó cả khóa và giá trị đều là str

bí danh của Union[Headers, Mapping[str, str], Iterable[Tuple[str, str]], SupportKeysAndGetItem]

websockets.datastructures.SupportsKeysAndGetItem = Dict-like types with keys[] -> str and __getitem__[key: str] -> str methods.

Extensions

Giao thức WebSocket hỗ trợ các phần mở rộng

At the time of writing, there's only one registered extension with a public specification, WebSocket Per-Message Deflate

Per-Message Deflate

websockets. extensions. permessage_deflate implements WebSocket Per-Message Deflate

This extension is specified in RFC 7692

Refer to the topic guide on compression to learn more about tuning compression settings

lớp websocket. extensions. permessage_deflate. ClientPerMessageDeflateFactory[server_no_context_takeover=False, client_no_context_takeover=False, server_max_window_bits=None, client_max_window_bits=True, nén_settings=None]Nhà máy tiện ích mở rộng phía máy khách cho tiện ích Xả hơi theo mỗi thông báo

Parameters behave as described in section 7. 1 of RFC 7692

Set them to True to include them in the negotiation offer without a value or to an integer value to include them with this value

Parameters

  • server_no_context_takeover [bool] -- ngăn không cho máy chủ sử dụng tiếp quản ngữ cảnh
  • client_no_context_takeover [bool] -- prevent client from using context takeover
  • server_max_window_bits [Tùy chọn[int]] -- kích thước tối đa của cửa sổ trượt LZ77 của máy chủ theo bit, từ 8 đến 15
  • client_max_window_bits [Optional[Union[int, bool]]] -- maximum size of the client's LZ77 sliding window in bits, between 8 and 15, or True to indicate support without setting a limit
  • nén_settings [Tùy chọn[Dict[str, Any]]] -- đối số từ khóa bổ sung cho zlib. compressobj[], excluding wbits

lớp websocket. phần mở rộng. permessage_deflate. ServerPerMessageDeflateFactory[server_no_context_takeover=False, client_no_context_takeover=False, server_max_window_bits=None, client_max_window_bits=None, compress_settings=None, require_client_max_window_bits=False]Server-side extension factory for the Per-Message Deflate extension

Parameters behave as described in section 7. 1 of RFC 7692

Set them to True to include them in the negotiation offer without a value or to an integer value to include them with this value

Parameters

  • server_no_context_takeover [bool] -- ngăn không cho máy chủ sử dụng tiếp quản ngữ cảnh
  • client_no_context_takeover [bool] -- prevent client from using context takeover
  • server_max_window_bits [Tùy chọn[int]] -- kích thước tối đa của cửa sổ trượt LZ77 của máy chủ theo bit, từ 8 đến 15
  • client_max_window_bits [Optional[int]] -- maximum size of the client's LZ77 sliding window in bits, between 8 and 15
  • nén_settings [Tùy chọn[Dict[str, Any]]] -- đối số từ khóa bổ sung cho zlib. compressobj[], excluding wbits
  • require_client_max_window_bits [bool] -- do not enable compression at all if client doesn't advertise support for client_max_window_bits; the default behavior is to enable compression without enforcing client_max_window_bits

lớp cơ sở

websockets. tiện ích mở rộng xác định các lớp cơ sở để triển khai tiện ích mở rộng

Tham khảo hướng dẫn cách thực hiện trên tiện ích mở rộng để tìm hiểu thêm về cách viết tiện ích mở rộng

lớp websocket. extensions. ExtensionBase class for extensions

Tên. ExtensionNameSố nhận dạng tiện ích mở rộng

giải mã[khung, *, max_size=None]Giải mã khung đến

Parameters

  • frame [Frame] -- incoming frame
  • max_size [Optional[int]] -- maximum payload size in bytes

ReturnsDecoded khung. Kiểu trả vềFrameRaisesPayloadTooBig -- nếu giải mã tải trọng vượt quá max_size

encode[frame]Encode an outgoing frame

Khung thông số [Khung] -- khung gửi đi. Trả vềKhung được mã hóa. Return typeFrame

class websockets. phần mở rộng. ClientExtensionFactoryBase class for client-side extension factories

Tên. ExtensionNameSố nhận dạng tiện ích mở rộng

get_request_params[]Tạo tham số để gửi đến máy chủ cho tiện ích mở rộng này

ReturnsParameters to send to the server. Kiểu trả vềDanh sách[Tham số mở rộng]

process_response_params[params, accepted_extensions]Process parameters received from the server

Parameters

  • params [Sequence[ExtensionParameter]] -- parameters received from the server for this extension
  • accepted_extensions [Sequence[Extension]] -- list of previously accepted extensions

Trả vềMột phiên bản tiện ích mở rộng. Return typeExtensionRaisesNegotiationError -- if parameters aren't acceptable

class websockets. extensions. Lớp ServerExtensionFactoryBase dành cho các nhà máy mở rộng phía máy chủ

process_request_params[params, accept_extensions]Thông số quy trình nhận được từ máy khách

Parameters

  • params [Sequence[ExtensionParameter]] -- parameters received from the client for this extension
  • accepted_extensions [Sequence[Extension]] -- list of previously accepted extensions

ReturnsĐể chấp nhận phiếu mua hàng, các tham số để gửi tới khách hàng cho tiện ích mở rộng này và phiên bản tiện ích mở rộng. Trả về typeTuple[List[ExtensionParameter], Extension]RaisesNegotiationError -- để từ chối ưu đãi, nếu thông số nhận được từ
khách hàng không được chấp nhận.

Hạn chế

Client

The client doesn't attempt to guarantee that there is no more than one connection to a given IP address in a CONNECTING state. Hành vi này được ủy quyền bởi RFC 6455. However, connect[] isn't the right layer for enforcing this constraint. Đó là trách nhiệm của người gọi

The client doesn't support connecting through a HTTP proxy [issue 364] or a SOCKS proxy [issue 475]

Server

At this time, there are no known limitations affecting only the server

Both sides

There is no way to control compression of outgoing frames on a per-frame basis [issue 538]. If compression is enabled, all frames are compressed

Không có cách nào để nhận từng đoạn tin nhắn bị phân mảnh khi nó đến [số 479]. websockets luôn tập hợp lại các tin nhắn bị phân mảnh trước khi trả lại chúng

Public API documented in the API reference are subject to the backwards-compatibility policy

Mọi thứ không được liệt kê trong tham chiếu API đều là API riêng tư. Không có gì đảm bảo về hành vi hoặc khả năng tương thích ngược cho các API riêng tư

Convenience imports are incompatible with some development tools

Để thuận tiện, hầu hết các API công khai có thể được nhập từ gói websockets. However, this is incompatible with static code analysis

It may break auto-completion and contextual documentation in IDEs, type checking with mypy, etc. Nếu bạn đang sử dụng các công cụ như vậy, hãy tuân theo đường dẫn nhập đầy đủ

HƯỚNG DẪN CHỦ ĐỀ

Get a deeper understanding of how websockets is built and why

Deployment

When you deploy your websockets server to production, at a high level, your architecture will almost certainly look like the following diagram. [image]

The basic unit for scaling a websockets server is "one server process". Each blue box in the diagram represents one server process

There's more variation in routing. Mặc dù lớp định tuyến được hiển thị dưới dạng một hộp lớn, nhưng nó có khả năng liên quan đến một số hệ thống con

Khi bạn thiết kế triển khai, bạn nên xem xét hai câu hỏi

1. Tôi sẽ chạy số lượng quy trình máy chủ thích hợp như thế nào?2. How will I route incoming connections to these processes?

Những câu hỏi này có liên quan chặt chẽ. Có rất nhiều câu trả lời có thể chấp nhận được, tùy thuộc vào mục tiêu và giới hạn của bạn

You can find a few concrete examples in the deployment how-to guides

Chạy quy trình máy chủ

How many processes do I need?

Thông thường, một quy trình máy chủ sẽ quản lý vài trăm hoặc hàng nghìn kết nối, tùy thuộc vào tần suất gửi tin nhắn và khối lượng công việc mà chúng yêu cầu

CPU and memory usage increase with the number of connections to the server

CPU thường là yếu tố hạn chế. Nếu một quá trình máy chủ đạt 100% CPU, thì bạn đã đạt đến giới hạn. Bạn muốn giữ bao nhiêu khoảng trống tùy thuộc vào bạn

Once you know how many connections a server process can manage and how many connections you need to handle, you can calculate how many processes to run

You can also automate this calculation by configuring an autoscaler to keep CPU usage or connection count within acceptable limits

Không mở rộng quy mô với các chủ đề. Threads doesn't make sense for a server built with asyncio

Làm cách nào để chạy các quy trình?

Most solutions for running multiple instances of a server process fall into one of these three buckets

1. Running N processes on a platform

  • a Kubernetes Deployment
  • its equivalent on a Platform as a Service provider

2. Chạy N máy chủ

  • an AWS Auto Scaling group, a GCP Managed instance group, etc
  • a fixed set of long-lived servers

3. Chạy N quy trình trên máy chủ

•tốt nhất là thông qua người quản lý quy trình hoặc người giám sát

Tùy chọn 1 là dễ nhất khi bạn có quyền truy cập vào một nền tảng như vậy

Tùy chọn 2 hầu như luôn kết hợp với tùy chọn 3

How do I start a process?

Run a Python program that invokes serve[]. That's it

Không chạy máy chủ ASGI như Uvicorn, Hypercorn hoặc Daphne. Chúng là những lựa chọn thay thế cho ổ cắm web, không phải phần bổ sung

Không chạy máy chủ WSGI như Gunicorn, Waitress hoặc mod_wsgi. They aren't designed to run WebSocket applications

Máy chủ ứng dụng xử lý các kết nối mạng và hiển thị API Python. You don't need one because websockets handles network connections directly

How do I stop a process?

Process managers send the SIGTERM signal to terminate processes. Bắt tín hiệu này và thoát khỏi máy chủ để đảm bảo tắt máy hiệu quả

Here's an example

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
18

When exiting the context manager, serve[] closes all connections with code 1001 [going away]. As a consequence

  • If the connection handler is awaiting recv[], it receives a ConnectionClosedOK exception. Nó có thể bắt ngoại lệ và dọn sạch trước khi thoát
  • Otherwise, it should be waiting on wait_closed[], so it can receive the ConnectionClosedOK exception and exit

Ví dụ này dễ dàng được điều chỉnh để xử lý các tín hiệu khác

Nếu bạn ghi đè trình xử lý tín hiệu mặc định cho SIGINT, điều này làm tăng KeyboardInterrupt, hãy lưu ý rằng bạn sẽ không thể ngắt chương trình bằng Ctrl-C nữa khi chương trình bị mắc kẹt trong một vòng lặp

kết nối định tuyến

What does routing involve?

Since the routing layer is directly exposed to the Internet, it should provide appropriate protection against threats ranging from Internet background noise to targeted attacks

You should always secure WebSocket connections with TLS. Vì lớp định tuyến mang tên miền công cộng nên nó sẽ chấm dứt các kết nối TLS

Cuối cùng, nó phải định tuyến các kết nối đến các quy trình của máy chủ, cân bằng các kết nối mới giữa chúng

How do I route connections?

Dưới đây là các giải pháp cân bằng tải điển hình, phù hợp với cách thức chạy các quy trình

1. If you're running on a platform, it comes with a routing layer

  • a Kubernetes Ingress and Service
  • a service mesh. Istio, Consul, Linkerd, etc
  • the routing mesh of a Platform as a Service

2. If you're running N servers, you may load balance with

  • a cloud load balancer. AWS Elastic Load Balancing, GCP Cloud Load Balancing, etc
  • A software load balancer. HAProxy, NGINX, etc

3. If you're running N processes on a server, you may load balance with

  • A software load balancer. HAProxy, NGINX, etc
  • The operating system — all processes listen on the same port

You may trust the load balancer to handle encryption and to provide security. You may add another layer in front of the load balancer for these purposes

There are many possibilities. Don't add layers that you don't need, though

Làm cách nào để thực hiện kiểm tra sức khỏe?

Load balancers need a way to check whether server processes are up and running to avoid routing connections to a non-functional backend

websockets provide minimal support for responding to HTTP requests with the process_request[] hook

Here's an example

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
46

Logging

Logs contents

When you run a WebSocket client, your code calls coroutines provided by websockets

If an error occurs, websockets tells you by raising an exception. For example, it raises a ConnectionClosed exception if the other side closes the connection

Khi bạn chạy máy chủ WebSocket, websockets chấp nhận kết nối, thực hiện bắt tay mở, chạy quy trình xử lý kết nối mà bạn đã cung cấp và thực hiện bắt tay đóng

Given this inversion of control, if an error happens in the opening handshake or if the connection handler crashes, there is no way to raise an exception that you can handle

Logs tell you about these errors

Besides errors, you may want to record the activity of the server

In a request/response protocol such as HTTP, there's an obvious way to record activity. log one event per request/response. Unfortunately, this solution doesn't work well for a bidirectional protocol such as WebSocket

Instead, when running as a server, websockets logs one event when a connection is established and another event when a connection is closed

By default, websockets doesn't log an event for every message. That would be excessive for many applications exchanging small messages at a fast rate. If you need this level of detail, you could add logging in your own code

Finally, you can enable debug logs to get details about everything websockets is doing. Điều này có thể hữu ích khi phát triển máy khách cũng như máy chủ

See log levels below for a list of events logged by websockets logs at each log level

Định cấu hình ghi nhật ký

websockets dựa vào mô-đun ghi nhật ký từ thư viện chuẩn để tối đa hóa khả năng tương thích và tích hợp độc đáo với các thư viện khác

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
47

websockets logs to the "websockets. client" and "websockets. máy chủ" logger

websockets doesn't provide a default logging configuration because requirements vary a lot depending on the environment

Here's a basic configuration for a server in production

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
48

Here's how to enable debug logs for development

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
49

Hơn nữa, websockets thêm thuộc tính websocket vào các bản ghi nhật ký, vì vậy bạn có thể bao gồm thông tin bổ sung về kết nối hiện tại trong nhật ký

You could attempt to add information with a formatter

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
50

However, this technique runs into two problems

  • The formatter applies to all records. It will crash if it receives a record without a websocket attribute. For example, this happens when logging that the server starts because there is no current connection
  • Even with str. format[] style, you're restricted to attribute and index lookups, which isn't enough to implement some fairly simple requirements

There's a better way. connect[] and serve[] accept a logger argument to override the default Logger. You can set logger to a LoggerAdapter that enriches logs

For example, if the server is behind a reverse proxy, remote_address gives the IP address of the proxy, which isn't useful. IP addresses of clients are provided in a HTTP header set by the proxy

Đây là cách đưa chúng vào nhật ký, giả sử chúng nằm trong tiêu đề X-Forwarded-For

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
51

Logging to JSON

Even though logging predates structured logging, it's still possible to output logs as JSON with a bit of effort

First, we need a Formatter that renders JSON

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
52

Then, we configure logging to apply this formatter

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
53

Finally, we populate the event_data custom attribute in log records with a LoggerAdapter

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
54

Disable logging

If your application doesn't configure logging, Python outputs messages of severity WARNING and higher to stderr. As a consequence, you will see a message and a stack trace if a connection handler coroutine crashes or if you hit a bug in websockets

If you want to disable this behavior for websockets, you can add a NullHandler

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
55

Additionally, if your application configures logging, you must disable propagation to the root logger, or else its handlers could output logs

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
56

Alternatively, you could set the log level to CRITICAL for the "websockets" logger, as the highest level currently used is ERROR

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
57

Or you could configure a filter to drop all messages

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
58

Log levels

Here's what websockets logs at each level

ERROR

  • Exceptions raised by connection handler coroutines in servers
  • Exceptions resulting from bugs in websockets

THÔNG TIN

  • Server starting and stopping
  • Server establishing and closing connections
  • Client reconnecting automatically

DEBUG

  • Changes to the state of connections
  • Handshake requests and responses
  • All frames sent and received
  • Steps to close a connection
  • Keepalive pings and pongs
  • Errors handled transparently

Debug messages have cute prefixes that make logs easier to scan

  • > - send something
  • < - receive something
  • = - set connection state
  • x - shut down connection
  • % - quản lý ping và pong
  • - handle errors and timeouts

Authentication

The WebSocket protocol was designed for creating web applications that need bidirectional communication between clients running in browsers and servers

In most practical use cases, WebSocket servers need to authenticate clients in order to route communications appropriately and securely

RFC 6455 stays elusive when it comes to authentication

This protocol doesn't prescribe any particular way that servers can authenticate clients during the WebSocket handshake. The WebSocket server can use any client authentication mechanism available to a generic HTTP server, such as cookies, HTTP authentication, or TLS authentication

None of these three mechanisms works well in practice. Using cookies is cumbersome, HTTP authentication isn't supported by all mainstream browsers, and TLS authentication in a browser is an esoteric user experience

Fortunately, there are better alternatives. Let's discuss them

System design

Consider a setup where the WebSocket server is separate from the HTTP server

Most servers built with websockets to complement a web application adopt this design because websockets doesn't aim at supporting HTTP

The following diagram illustrates the authentication flow. [image]

Assuming the current user is authenticated with the HTTP server [1], the application needs to obtain credentials from the HTTP server [2] in order to send them to the WebSocket server [3], who can check them against the database of user accounts [4]

Usernames and passwords aren't a good choice of credentials here, if only because passwords aren't available in clear text in the database

Mã thông báo được liên kết với tài khoản người dùng là lựa chọn tốt hơn. These tokens must be impossible to forge by an attacker. Để bảo mật hơn, chúng có thể tồn tại trong thời gian ngắn hoặc thậm chí sử dụng một lần

Sending credentials

Assume the web application obtained authentication credentials, likely a token, from the HTTP server. Có bốn tùy chọn để chuyển chúng đến máy chủ WebSocket

1. Sending credentials as the first message in the WebSocket connection

This is fully reliable and the most secure mechanism in this discussion. Nó có hai nhược điểm nhỏ

  • Authentication is performed at the application layer. Ideally, it would be managed at the protocol layer
  • Authentication is performed after the WebSocket handshake, making it impossible to monitor authentication failures with HTTP response codes

2. Adding credentials to the WebSocket URI in a query parameter

This is also fully reliable but less secure. Indeed, it has a major downside

•URIs end up in logs, which leaks credentials. Even if that risk could be lowered with single-use tokens, it is usually considered unacceptable

Authentication is still performed at the application layer but it can happen before the WebSocket handshake, which improves separation of concerns and enables responding to authentication failures with HTTP 401

3. Setting a cookie on the domain of the WebSocket URI

Cookies are undoubtedly the most common and hardened mechanism for sending credentials from a web application to a server. In a HTTP application, credentials would be a session identifier or a serialized, signed session

Unfortunately, when the WebSocket server runs on a different domain from the web application, this idea bumps into the Same-Origin Policy. For security reasons, setting a cookie on a different origin is impossible

The proper workaround consists in

  • creating a hidden iframe served from the domain of the WebSocket server
  • sending the token to the iframe with postMessage
  • setting the cookie in the iframe

before opening the WebSocket connection

Chia sẻ một tên miền mẹ [e. g. example. com] between the HTTP server [e. g. www. example. com] and the WebSocket server [e. g. ws. thí dụ. com] and setting the cookie on that parent domain would work too

However, the cookie would be shared with all subdomains of the parent domain. For a cookie containing credentials, this is unacceptable

4. Adding credentials to the WebSocket URI in user information

Về mặt lý thuyết, để trình duyệt thực hiện HTTP Basic Auth là một ý tưởng hay

In practice it doesn't work due to poor support in browsers

As of May 2021

  • Chrome 90 behaves as expected
  • Firefox 88 caches credentials too aggressively

    When connecting again to the same server with new credentials, it reuses the old credentials, which may be expired, resulting in an HTTP 401. Then the next connection succeeds. Có lẽ lỗi xóa bộ nhớ cache

    When tokens are short-lived or single-use, this bug produces an interesting effect. mọi kết nối WebSocket khác không thành công

  • Safari 14 ignores credentials entirely

Two other options are off the table

1. Setting a custom HTTP header

This would be the most elegant mechanism, solving all issues with the options discussed above

Unfortunately, it doesn't work because the WebSocket API doesn't support setting custom headers

2. Authenticating with a TLS certificate

While this is suggested by the RFC, installing a TLS certificate is too far from the mainstream experience of browser users. This could make sense in high security contexts. I hope developers working on such projects don't take security advice from the documentation of random open source projects

Let's experiment

The experiments/authentication directory demonstrates these techniques

Run the experiment in an environment where websockets is installed

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
59

When you browse to the HTTP server at http. //localhost. 8000/ and you submit a username, the server creates a token and returns a testing web page

This page opens WebSocket connections to four WebSocket servers running on four different origins. It attempts to authenticate with the token in four different ways

First message

As soon as the connection is open, the client sends a message containing the token

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
60

At the beginning of the connection handler, the server receives this message and authenticates the user. If authentication fails, the server closes the connection

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
61

Tham số truy vấn

Máy khách thêm mã thông báo vào URI WebSocket trong tham số truy vấn trước khi mở kết nối

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
62

The server intercepts the HTTP request, extracts the token and authenticates the user. If authentication fails, it returns a HTTP 401

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
63

Cookie

The client sets a cookie containing the token before opening the connection

The cookie must be set by an iframe loaded from the same origin as the WebSocket server. This requires passing the token to this iframe

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
64

This sequence must be synchronized between the main window and the iframe. This involves several events. Look at the full implementation for details

The server intercepts the HTTP request, extracts the token and authenticates the user. If authentication fails, it returns a HTTP 401

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
65

User information

The client adds the token to the WebSocket URI in user information before opening the connection

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
66

Since HTTP Basic Auth is designed to accept a username and a password rather than a token, we send token as username and the token as password

The server intercepts the HTTP request, extracts the token and authenticates the user. If authentication fails, it returns a HTTP 401

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
67

Machine-to-machine authentication

When the WebSocket client is a standalone program rather than a script running in a browser, there are far fewer constraints. HTTP Authentication is the best solution in this scenario

To authenticate a websockets client with HTTP Basic Authentication [RFC 7617], include the credentials in the URI

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
68

[You must quote[] username and password if they contain unsafe characters. ]

To authenticate a websockets client with HTTP Bearer Authentication [RFC 6750], add a suitable Authorization header

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
69

Broadcasting messages

If you just want to send a message to all connected clients,use broadcast[]. "

If you want to learn about its design in depth, continue reading this document

WebSocket servers often send the same message to all connected clients or to a subset of clients for which the message is relevant

Let's explore options for broadcasting a message, explain the design of broadcast[], and discuss alternatives

For each option, we'll provide a connection handler called handler[] and a function or coroutine called broadcast[] that sends a message to all connected clients

Integrating them is left as an exercise for the reader. You could start with

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
70

broadcast_messages[] must yield control to the event loop between each message, or else it will never let the server run. That's why it includes await asyncio. sleep[1]

A complete example is available in the experiments/broadcast directory

The naive way

The most obvious way to send a message to all connected clients consists in keeping track of them and sending the message to each of them

Here's a connection handler that registers clients in a global variable

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
71

This implementation assumes that the client will never send any messages. If you'd rather not make this assumption, you can change

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
72

to

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
73

Here's a coroutine that broadcasts a message to all clients

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
74

There are two tricks in this version of broadcast[]

Đầu tiên, nó tạo một bản sao của CLIENTS trước khi lặp lại nó. Mặt khác, nếu máy khách kết nối hoặc ngắt kết nối trong khi quảng bá [] đang chạy, vòng lặp sẽ không thành công với

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
75

Thứ hai, nó bỏ qua các ngoại lệ của ConnectionClosed vì một máy khách có thể ngắt kết nối giữa thời điểm quảng bá[] tạo một bản sao của CLIENTS và thời điểm nó gửi một thông báo đến máy khách này. This is fine. a client that disconnected doesn't belongs to "all connected clients" anymore

The naive way can be very fast. Thật vậy, nếu tất cả các kết nối có đủ dung lượng trống trong bộ đệm ghi của chúng, hãy chờ websocket. gửi [tin nhắn] viết tin nhắn và trả về ngay lập tức, vì nó không cần đợi bộ đệm cạn kiệt. Trong trường hợp này, quảng bá [] không mang lại quyền kiểm soát cho vòng lặp sự kiện, giúp giảm thiểu chi phí hoạt động

The naive way can also fail badly. If the write buffer of a connection reaches write_limit, broadcast[] waits for the buffer to drain before sending the message to other clients. This can cause a massive drop in performance

As a consequence, this pattern works only when write buffers never fill up, which is usually outside of the control of the server

If you know for sure that you will never write more than write_limit bytes within ping_interval + ping_timeout, then websockets will terminate slow connections before the write buffer has time to fill up

Không đặt các giá trị cực cao write_limit, ping_interval và ping_timeout để đảm bảo rằng điều kiện này được duy trì. Đặt các giá trị hợp lý và thay vào đó sử dụng chức năng phát sóng [] tích hợp

cách đồng thời

Cách ngây thơ không hoạt động tốt vì nó được ghi tuần tự, trong khi toàn bộ quan điểm của I/O không đồng bộ là thực hiện I/O đồng thời

Let's modify broadcast[] to send messages concurrently

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
76

We move the error handling logic in a new coroutine and we schedule a Task to run it instead of executing it immediately

Vì quảng bá [] không còn chờ các coroutine, chúng tôi có thể biến nó thành một chức năng thay vì một coroutine và loại bỏ bản sao của CLIENTS

This version of broadcast[] makes clients independent from one another. a slow client won't block others. As a side effect, it makes messages independent from one another

If you broadcast several messages, there is no strong guarantee that they will be sent in the expected order. Fortunately, the event loop runs tasks in the order in which they are created, so the order is correct in practice

Về mặt kỹ thuật, đây là chi tiết triển khai của vòng lặp sự kiện. Tuy nhiên, có vẻ như một vòng lặp sự kiện sẽ không thể chạy các tác vụ theo thứ tự khác với FIFO

Nếu bạn muốn thực thi lệnh mà không cần dựa vào chi tiết triển khai này, bạn có thể đợi cho đến khi tất cả khách hàng nhận được thông báo

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
77

Tuy nhiên, điều này không thực sự hiệu quả trong thực tế. Quite often, it will block until the slowest client times out

Backpressure meets broadcast

At this point, it becomes apparent that backpressure, usually a good practice, doesn't work well when broadcasting a message to thousands of clients

When you're sending messages to a single client, you don't want to send them faster than the network can transfer them and the client accept them. This is why send[] checks if the write buffer is full and, if it is, waits until it drain, giving the network and the client time to catch up. This provides backpressure

Without backpressure, you could pile up data in the write buffer until the server process runs out of memory and the operating system kills it

The send[] API is designed to enforce backpressure by default. Điều này giúp người dùng ổ cắm web viết các chương trình mạnh mẽ ngay cả khi họ chưa bao giờ nghe nói về áp suất ngược

For comparison, asyncio. StreamWriter yêu cầu người dùng hiểu về áp suất ngược và chờ đợi cống [] một cách rõ ràng sau mỗi lần ghi []

When broadcasting messages, backpressure consists in slowing down all clients in an attempt to let the slowest client catch up. Với hàng nghìn khách hàng, khách hàng chậm nhất có thể đã hết thời gian chờ và sẽ không nhận được tin nhắn. Vì vậy, thật vô nghĩa khi đồng bộ hóa với ứng dụng khách chậm nhất

Vậy thì làm cách nào để tránh hết bộ nhớ khi các máy khách chậm không thể theo kịp tốc độ phát sóng?

If a client gets too far behind, eventually it reaches the limit defined by ping_timeout and websockets terminates the connection. You can read the discussion of keepalive and timeouts for details

How broadcast[] works

The built-in broadcast[] function is similar to the naive way. The main difference is that it doesn't apply backpressure

This provides the best performance by avoiding the overhead of scheduling and running one task per client

Also, when sending text messages, encoding to UTF-8 happens only once rather than once per client, providing a small performance gain

hàng đợi cho mỗi khách hàng

At this point, we deal with slow clients rather brutally. we disconnect then

Chúng ta có thể làm tốt hơn không?

To implement this logic, we can create a queue of messages for each client and run a task that gets messages from the queue and sends them to the client

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
78

Sau đó, chúng tôi có thể phát một tin nhắn bằng cách đẩy nó vào tất cả các hàng đợi

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
79

The queues provide an additional buffer between the broadcast[] function and clients. Điều này giúp dễ dàng hỗ trợ các máy khách chậm mà không sử dụng bộ nhớ quá mức vì các thông báo được xếp hàng đợi không bị trùng lặp để ghi vào bộ đệm cho đến khi relay[] xử lý chúng

Publish–subscribe

Can we avoid centralizing the list of connected clients in a global variable?

If each client subscribes to a stream a messages, then broadcasting becomes as simple as publishing a message to the stream

Đây là luồng thông báo hỗ trợ nhiều người tiêu dùng

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
80

The stream is implemented as a linked list of futures. Không cần thiết phải đồng bộ hóa người tiêu dùng. They can read the stream at their own pace, independently from one another. Sau khi tất cả người tiêu dùng đọc một tin nhắn, không còn tài liệu tham khảo nào, do đó, trình thu gom rác sẽ xóa nó

Trình xử lý kết nối đăng ký luồng và gửi tin nhắn

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
81

Chức năng phát sóng xuất bản lên luồng

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
82

Giống như hàng đợi cho mỗi máy khách, phiên bản này hỗ trợ các máy khách chậm với mức sử dụng bộ nhớ hạn chế. Không giống như hàng đợi theo từng khách hàng, thật khó để biết khách hàng đang ở phía sau bao xa. Lớp PubSub có thể được mở rộng hoặc tái cấu trúc để cung cấp thông tin này

Vòng lặp for đã biến mất khỏi phiên bản hàm Broadcast[] này. However, there's still a for loop iterating on all clients hidden deep inside asyncio. Khi xuất bản [] đặt kết quả của tương lai người phục vụ, asyncio lặp lại các cuộc gọi lại đã đăng ký với tương lai này và lên lịch cho chúng. Đây là cách trình xử lý kết nối nhận giá trị tiếp theo từ trình vòng lặp không đồng bộ được trả về bởi subscribe[]

cân nhắc hiệu suất

Chức năng phát sóng [] tích hợp sẽ gửi tất cả các tin nhắn mà không mang lại quyền kiểm soát cho vòng lặp sự kiện. Cách ngây thơ cũng vậy khi mạng và máy khách nhanh và đáng tin cậy

Đối với mỗi máy khách, một khung WebSocket được chuẩn bị và gửi đến mạng. Đây là số lượng công việc tối thiểu cần thiết để phát một tin nhắn

Sẽ rất hấp dẫn khi chuẩn bị một khung và sử dụng lại nó cho tất cả các kết nối. Tuy nhiên, điều này là không thể nói chung vì hai lý do

  • Clients can negotiate different extensions. You would have to enforce the same extensions with the same parameters. For example, you would have to select some compression settings and reject clients that cannot support these settings
  • Extensions can be stateful, producing different encodings of the same message depending on previous messages. For example, you would have to disable context takeover to make compression stateless, resulting in poor compression rates

Tất cả các mẫu khác được thảo luận ở trên mang lại quyền kiểm soát cho vòng lặp sự kiện một lần cho mỗi khách hàng vì các thông báo được gửi bởi các tác vụ khác nhau. Điều này làm cho chúng chậm hơn chức năng phát sóng [] tích hợp

There is no major difference between the performance of per-message queues and publish–subscribe

Compression

Hầu hết các máy chủ WebSocket trao đổi thông báo JSON vì chúng thuận tiện để phân tích cú pháp và tuần tự hóa trong trình duyệt. Các tin nhắn này chứa dữ liệu văn bản và có xu hướng lặp lại

Điều này làm cho luồng tin nhắn có khả năng nén cao. Enabling compression can reduce network traffic by more than 80%

Có một tiêu chuẩn để nén tin nhắn. RFC 7692 định nghĩa WebSocket Per-Message Deflate, một phần mở rộng nén dựa trên thuật toán Deflate

Định cấu hình nén

connect[] và serve[] cho phép nén theo mặc định vì việc giảm băng thông mạng thường đáng giá bộ nhớ bổ sung và chi phí CPU

Nếu bạn muốn tắt tính năng nén, hãy đặt nén=None

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
83

If you want to customize compression settings, you can enable the Per-Message Deflate extension explicitly with ClientPerMessageDeflateFactory or ServerPerMessageDeflateFactory

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
84

Các giá trị Window Bits và Memory Level trong các ví dụ này làm giảm mức sử dụng bộ nhớ với chi phí là tốc độ nén

Compression settings

Khi một máy khách và một máy chủ kích hoạt tiện ích mở rộng Per-Message Deflate, chúng sẽ thương lượng hai tham số để đảm bảo khả năng tương thích giữa nén và giải nén. Các tham số này ảnh hưởng đến sự đánh đổi giữa tốc độ nén và mức sử dụng bộ nhớ cho cả hai bên

  • Context Takeover có nghĩa là bối cảnh nén được giữ lại giữa các thông báo. Nói cách khác, tính năng nén được áp dụng cho luồng tin nhắn chứ không phải cho từng tin nhắn riêng lẻ

    Tiếp quản ngữ cảnh vẫn phải được kích hoạt để có được hiệu suất tốt trên các ứng dụng gửi luồng thông báo có cấu trúc tương tự, nghĩa là hầu hết các ứng dụng

    Điều này yêu cầu giữ lại ngữ cảnh và trạng thái nén giữa các thông báo, điều này làm tăng dung lượng bộ nhớ của kết nối

  • Window Bits kiểm soát kích thước của bối cảnh nén. Nó phải là một số nguyên trong khoảng từ 9 [mức sử dụng bộ nhớ thấp nhất] đến 15 [nén tốt nhất]. Setting it to 8 is possible but rejected by some versions of zlib

    Về phía máy chủ, websockets mặc định là 12. Cụ thể, kích thước cửa sổ nén [server to client] luôn là 12 trong khi kích thước cửa sổ giải nén [client to server] có thể là 12 hoặc 15 tùy thuộc vào việc client có hỗ trợ cấu hình hay không.

    Về phía máy khách, ổ cắm web cho phép máy chủ chọn một giá trị phù hợp, giá trị này có tác dụng tương tự như mặc định là 15

zlib cung cấp các tham số bổ sung để điều chỉnh nén. Chúng kiểm soát sự đánh đổi giữa tốc độ nén, mức sử dụng bộ nhớ và mức sử dụng CPU chỉ để nén. They're transparent for decompressing. Trừ khi được đề cập khác, websockets kế thừa các giá trị mặc định của nénobj[]

  • Mức bộ nhớ kiểm soát kích thước của trạng thái nén. Nó phải là một số nguyên trong khoảng từ 1 [mức sử dụng bộ nhớ thấp nhất] đến 9 [nén tốt nhất]

    ổ cắm web mặc định là 5. This is lower than zlib's default of 8. Mức bộ nhớ thấp hơn không chỉ làm giảm mức sử dụng bộ nhớ mà còn có thể tăng tốc độ nhờ vị trí bộ nhớ

  • Mức nén kiểm soát nỗ lực tối ưu hóa quá trình nén. It must be an integer between 1 [lowest CPU usage] and 9 [best compression]
  • Chiến lược chọn chiến lược nén. The best choice depends on the type of data being compressed

điều chỉnh nén

For servers

Theo mặc định, ổ cắm web cho phép nén với các cài đặt bảo thủ nhằm tối ưu hóa việc sử dụng bộ nhớ với chi phí là tốc độ nén kém hơn một chút. Window Bits = 12 and Memory Level = 5. This strikes a good balance for small messages that are typical of WebSocket servers

Đây là cách các cài đặt nén khác nhau ảnh hưởng đến việc sử dụng bộ nhớ của một kết nối trên hệ thống 64 bit, cũng như điểm chuẩn về kích thước nén và thời gian nén cho một kho tài liệu JSON nhỏ

Window BitsMức bộ nhớMức sử dụng bộ nhớKích thước so với. defaultTime vs. default158322 KiB-4. 0%+15%147178 KiB-2. 6%+10%136106 KiB-1. 4%+5%12570 KiB==11452 KiB+3. 7%-5%10343 KiB+90%+50%9239 KiB+160%+100%——19 KiB+452%—

Window Bits and Memory Level don't have to move in lockstep. However, other combinations don't yield significantly better results than those shown above

Compressed size and compression time depend heavily on the kind of messages exchanged by the application so this example may not apply to your use case

You can adapt compression/benchmark. py by creating a list of typical messages and passing it to the _run function

Window Bits = 11 and Memory Level = 4 looks like the sweet spot in this table

websockets defaults to Window Bits = 12 and Memory Level = 5 to stay away from Window Bits = 10 or Memory Level = 3 where performance craters, raising doubts on what could happen at Window Bits = 11 and Memory Level = 4 on a different corpus

Defaults must be safe for all applications, hence a more conservative choice

Điểm chuẩn tập trung vào nén vì nó đắt hơn giải nén. Thật vậy, bỏ qua các phân bổ nhỏ, việc sử dụng bộ nhớ lý thuyết là

  • [1 MỞ. trong connection_open[] chạy khi bắt tay mở hoàn tất và kết nối WebSocket được thiết lập — đừng nhầm với connection_made[] chạy khi kết nối TCP được thiết lập;
  • MỞ -> ĐÓNG. in write_frame[] immediately before sending a close frame; since receiving a close frame triggers sending a close frame, this does the right thing regardless of which side started the closing handshake; also in fail_connection[] which duplicates a few lines of code from write_close_frame[] and write_frame[];
  • * -> ĐÃ ĐÓNG. trong connection_lost[] luôn được gọi chính xác một lần khi đóng kết nối TCP

quân đoàn

The following diagram shows which coroutines are running at each stage of the connection lifecycle on the client side

Vòng đời giống hệt nhau ở phía máy chủ, ngoại trừ việc đảo ngược điều khiển làm cho kết nối [] tương đương ẩn

Các coroutines hiển thị màu xanh lá cây được gọi bởi ứng dụng. Multiple coroutines may interact with the WebSocket connection concurrently

Các coroutine được hiển thị bằng màu xám quản lý kết nối. Khi bắt tay mở thành công, connection_open[] bắt đầu hai tác vụ

  • transfer_data_task chạy transfer_data[] xử lý dữ liệu đến và cho phép recv[] sử dụng nó. Nó có thể bị hủy bỏ để chấm dứt kết nối. Nó không bao giờ thoát với một ngoại lệ khác với CancelledError. Xem chuyển dữ liệu bên dưới
  • keepalive_ping_task chạy keepalive_ping[] sẽ gửi các khung Ping đều đặn và đảm bảo nhận được các khung Pong tương ứng. It is canceled when the connection terminates. It never exits with an exception other than CancelledError
  • close_connection_task runs close_connection[] which waits for the data transfer to terminate, then takes care of closing the TCP connection. Nó không được hủy bỏ. It never exits with an exception. See connection termination below

Besides, fail_connection[] starts the same close_connection_task when the opening handshake fails, in order to close the TCP connection

Việc phân chia trách nhiệm giữa hai tác vụ giúp dễ dàng đảm bảo rằng ổ cắm web có thể chấm dứt kết nối

  • within a fixed timeout,
  • without leaking pending tasks,
  • without leaking open TCP connections,

regardless of whether the connection terminates normally or abnormally

transfer_data_task completes when no more data will be received on the connection. Trong trường hợp bình thường, nó thoát ra sau khi trao đổi các khung gần

close_connection_task hoàn thành khi đóng kết nối TCP

bắt tay mở đầu

websockets performs the opening handshake when establishing a WebSocket connection. On the client side, connect[] executes it before returning the protocol to the caller. On the server side, it's executed before passing the protocol to the ws_handler coroutine handling the connection

Mặc dù bắt tay mở không đối xứng — máy khách gửi yêu cầu Nâng cấp HTTP và máy chủ trả lời bằng phản hồi Giao thức chuyển đổi HTTP — ổ cắm web nhằm mục đích giữ cho việc triển khai của cả hai bên nhất quán với nhau

Về phía khách hàng, bắt tay[]

  • builds a HTTP request based on the uri and parameters passed to connect[];
  • ghi yêu cầu HTTP vào mạng;
  • đọc phản hồi HTTP từ mạng;
  • checks the HTTP response, validates extensions and subprotocol, and configures the protocol accordingly;
  • moves to the OPEN state

On the server side, handshake[]

  • reads a HTTP request from the network;
  • calls process_request[] which may abort the WebSocket handshake and return a HTTP response instead; this hook only makes sense on the server side;
  • kiểm tra yêu cầu HTTP, thương lượng các tiện ích mở rộng và giao thức con, đồng thời định cấu hình giao thức cho phù hợp;
  • builds a HTTP response based on the above and parameters passed to serve[];
  • writes the HTTP response to the network;
  • moves to the OPEN state;
  • returns the path part of the uri

Sự bất đối xứng đáng kể nhất giữa hai bên của cái bắt tay mở đầu nằm ở việc đàm phán về các phần mở rộng và ở mức độ thấp hơn là giao thức con. The server knows everything about both sides and decides what the parameters should be for the connection. Khách hàng chỉ cần áp dụng chúng

If anything goes wrong during the opening handshake, websockets fails the connection

Data transfer

Symmetry

Once the opening handshake has completed, the WebSocket protocol enters the data transfer phase. This part is almost symmetrical. There are only two differences between a server and a client

  • mặt nạ máy khách đến máy chủ. the client masks outgoing frames; the server unmasks incoming frames;
  • đóng kết nối TCP. the server closes the connection immediately; the client waits for the server to do it

These differences are so minor that all the logic for data framing, for sending and receiving data and for closing the connection is implemented in the same class, WebSocketCommonProtocol

The is_client attribute tells which side a protocol instance is managing. Thuộc tính này được định nghĩa trên các lớp WebSocketServerProtocol và WebSocketClientProtocol

Data flow

Sơ đồ sau đây cho thấy cách dữ liệu luân chuyển giữa một ứng dụng được xây dựng trên websocket và một điểm cuối từ xa. It applies regardless of which side is the server or the client

Các phương thức công khai được hiển thị bằng màu xanh lá cây, các phương thức riêng tư có màu vàng và bộ đệm màu cam. Methods related to connection termination are omitted; connection termination is discussed in another section below

Receiving data

The left side of the diagram shows how websockets receives data

Incoming data is written to a StreamReader in order to implement flow control and provide backpressure on the TCP connection

transfer_data_task, which is started when the WebSocket connection is established, processes this data

When it receives data frames, it reassembles fragments and puts the resulting messages in the messages queue

When it encounters a control frame

  • if it's a close frame, it starts the closing handshake;
  • if it's a ping frame, it answers with a pong frame;
  • nếu đó là khung pong, nó sẽ xác nhận lệnh ping tương ứng [trừ khi đó là lệnh pong không được yêu cầu]

Running this process in a task guarantees that control frames are processed promptly. Nếu không có nhiệm vụ như vậy, websockets sẽ phụ thuộc vào ứng dụng để điều khiển kết nối bằng cách có chính xác một coroutine đang chờ recv[] bất cứ lúc nào. Mặc dù điều này xảy ra một cách tự nhiên trong nhiều trường hợp sử dụng, nhưng không thể dựa vào nó

Then recv[] fetches the next message from the messages queue, with some complexity added for handling backpressure and termination correctly

Đang gửi dữ liệu

The right side of the diagram shows how websockets sends data

send[] writes one or several data frames containing the message. Trong khi gửi một tin nhắn bị phân mảnh, các cuộc gọi đồng thời gửi[] sẽ bị tạm dừng cho đến khi tất cả các mảnh được gửi. This makes concurrent calls safe

ping[] writes a ping frame and yields a Future which will be completed when a matching pong frame is received

pong[] writes a pong frame

close[] writes a close frame and waits for the TCP connection to terminate

Dữ liệu gửi đi được ghi vào StreamWriter để thực hiện kiểm soát luồng và cung cấp áp suất ngược từ kết nối TCP

Closing handshake

When the other side of the connection initiates the closing handshake, read_message[] receives a close frame while in the OPEN state. It moves to the CLOSING state, sends a close frame, and returns None, causing transfer_data_task to terminate

When this side of the connection initiates the closing handshake with close[], it moves to the CLOSING state and sends a close frame. When the other side sends a close frame, read_message[] receives it in the CLOSING state and returns None, also causing transfer_data_task to terminate

Nếu phía bên kia không gửi khung đóng trong thời gian chờ đóng của kết nối, ổ cắm web sẽ không kết nối được

Quá trình bắt tay kết thúc có thể mất tới 2 * close_timeout. một close_timeout để ghi khung đóng và một close_timeout để nhận khung đóng

Then websockets terminates the TCP connection

Connection termination

close_connection_task, được bắt đầu khi kết nối WebSocket được thiết lập, chịu trách nhiệm đóng kết nối TCP cuối cùng

First close_connection_task waits for transfer_data_task to terminate, which may happen as a result of

  • a successful closing handshake. as explained above, this exits the infinite loop in transfer_data_task;
  • a timeout while waiting for the closing handshake to complete. điều này hủy chuyển_dữ_liệu_tác vụ;
  • a protocol error, including connection errors. tùy thuộc vào ngoại lệ, transfer_data_task không kết nối được với mã phù hợp và thoát

close_connection_task tách biệt với transfer_data_task để dễ dàng triển khai thời gian chờ khi kết thúc bắt tay. Canceling transfer_data_task creates no risk of canceling close_connection_task and failing to close the TCP connection, thus leaking resources

Sau đó, close_connection_task hủy bỏ keepalive_ping[]. Nhiệm vụ này không có trách nhiệm tuân thủ giao thức. Chấm dứt nó để tránh rò rỉ nó là mối quan tâm duy nhất

Việc chấm dứt kết nối TCP có thể mất tới 2 * close_timeout ở phía máy chủ và 3 * close_timeout ở phía máy khách. Clients start by waiting for the server to close the connection, hence the extra close_timeout. Sau đó, cả hai bên thực hiện các bước sau cho đến khi mất kết nối TCP. half-closing the connection [only for non-TLS connections], closing the connection, aborting the connection. Tại thời điểm này, kết nối bị ngắt bất kể điều gì xảy ra trên mạng

Connection failure

Nếu quá trình bắt tay mở không hoàn tất thành công, ổ cắm web không kết nối được bằng cách đóng kết nối TCP

Once the opening handshake has completed, websockets fails the connection by canceling transfer_data_task and sending a close frame if appropriate

transfer_data_task thoát, bỏ chặn close_connection_task, đóng kết nối TCP

Server shutdown

WebSocketServer đóng không đồng bộ như asyncio. Server. The shutdown happen in two steps

1. Stop listening and accepting new connections;2. Đóng các kết nối đã thiết lập bằng mã đóng 1001 [sẽ ngừng hoạt động] hoặc, nếu quá trình bắt tay mở vẫn đang diễn ra, với mã trạng thái HTTP 503 [Dịch vụ không khả dụng]

The first call to close starts a task that performs this sequence. Further calls are ignored. This is the easiest way to make close and wait_closed idempotent

Cancellation

Mã người dùng

websockets provides a WebSocket application server. It manages connections and passes them to user-provided connection handlers. This is an inversion of control scenario. mã thư viện gọi mã người dùng

If a connection drops, the corresponding handler should terminate. If the server shuts down, all connection handlers must terminate. Việc hủy bỏ các trình xử lý kết nối sẽ chấm dứt chúng

However, using cancellation for this purpose would require all connection handlers to handle it properly. For example, if a connection handler starts some tasks, it should catch CancelledError, terminate or cancel these tasks, and then re-raise the exception

Cancellation is tricky in asyncio applications, especially when it interacts with finalization logic. In the example above, what if a handler gets interrupted with CancelledError while it's finalizing the tasks it started, after detecting that the connection dropped?

websockets considers that cancellation may only be triggered by the caller of a coroutine when it doesn't care about the results of that coroutine anymore. [Nguồn. Guido van Rossum]. Since connection handlers run arbitrary user code, websockets has no way of deciding whether that code is still doing something worth caring about

Vì những lý do này, ổ cắm web không bao giờ hủy bỏ trình xử lý kết nối. Thay vào đó, nó mong đợi chúng phát hiện khi kết nối bị đóng, thực thi logic hoàn thiện nếu cần và thoát

Conversely, cancellation isn't a concern for WebSocket clients because they don't involve inversion of control

Library

Hầu hết các API công khai của websockets là coroutine. Chúng có thể bị hủy, ví dụ nếu người dùng bắt đầu một tác vụ gọi các coroutine này và hủy tác vụ sau đó. websockets phải xử lý tình huống này

Cancellation during the opening handshake is handled like any other exception. the TCP connection is closed and the exception is re-raised. Điều này chỉ có thể xảy ra ở phía khách hàng. Về phía máy chủ, bắt tay mở được quản lý bởi websockets và không có gì dẫn đến việc hủy bỏ

Once the WebSocket connection is established, internal tasks transfer_data_task and close_connection_task mustn't get accidentally canceled if a coroutine that awaits them is canceled. Nói cách khác, chúng phải được bảo vệ khỏi sự hủy bỏ

recv[] chờ tin nhắn tiếp theo trong hàng đợi hoặc đợi transfer_data_task kết thúc, tùy theo điều kiện nào đến trước. It relies on wait[] for waiting on two futures in parallel. As a consequence, even though it's waiting on a Future signaling the next message and on transfer_data_task, it doesn't propagate cancellation to them

ensure_open[] được gọi bởi send[], ping[], và pong[]. When the connection state is CLOSING, it waits for transfer_data_task but shields it to prevent cancellation

close[] waits for the data transfer task to terminate with wait_for[]. If it's canceled or if the timeout elapses, transfer_data_task is canceled, which is correct at this point. close[] then waits for close_connection_task but shields it to prevent cancellation

close[] and fail_connection[] are the only places where transfer_data_task may be canceled

close_connection_task starts by waiting for transfer_data_task. Nó bắt CancelledError để ngăn quá trình hủy transfer_data_task truyền sang close_connection_task

Backpressure

GHI CHÚ

Phần này thảo luận về áp suất ngược từ quan điểm của máy chủ nhưng khái niệm này áp dụng cho các máy khách một cách đối xứng

Với cách triển khai ngây thơ, nếu máy chủ nhận đầu vào nhanh hơn khả năng xử lý của chúng hoặc nếu máy chủ tạo kết quả đầu ra nhanh hơn khả năng gửi chúng, thì dữ liệu sẽ tích tụ trong bộ đệm, cuối cùng khiến máy chủ hết bộ nhớ và gặp sự cố

Giải pháp cho vấn đề này là áp suất ngược. Bất kỳ phần nào của máy chủ nhận đầu vào nhanh hơn khả năng xử lý của chúng và gửi đầu ra phải truyền thông tin đó trở lại phần trước đó trong chuỗi

websockets is designed to make it easy to get backpressure right

For incoming data, websockets builds upon StreamReader which propagates backpressure to its own buffer and to the TCP stream. Các khung được phân tích cú pháp từ luồng đầu vào và được thêm vào hàng đợi có giới hạn. If the queue fills up, parsing halts until the application reads a frame

For outgoing data, websockets builds upon StreamWriter which implements flow control. Nếu bộ đệm đầu ra phát triển quá lớn, nó sẽ đợi cho đến khi chúng cạn kiệt. That's why all APIs that write frames are asynchronous

Tất nhiên, ứng dụng vẫn có thể tạo bộ đệm không giới hạn của riêng mình và phá vỡ áp suất ngược. Hãy cẩn thận với hàng đợi

bộ đệm

GHI CHÚ

This section discusses buffers from the perspective of a server but it applies to clients as well

Một hệ thống không đồng bộ hoạt động tốt nhất khi bộ đệm của nó hầu như luôn trống

For example, if a client sends data too fast for a server, the queue of incoming messages will be constantly full. Máy chủ sẽ luôn có 32 tin nhắn [theo mặc định] phía sau máy khách. This consumes memory and increases latency for no good reason. The problem is called bufferbloat

Nếu bộ đệm hầu như luôn đầy và vấn đề đó không thể được giải quyết bằng cách thêm dung lượng — thường là do hệ thống bị tắc nghẽn bởi đầu ra và liên tục được điều chỉnh bởi áp suất ngược — thì việc giảm kích thước bộ đệm sẽ giảm thiểu hậu quả tiêu cực

By default websockets has rather high limits. Bạn có thể giảm chúng theo đặc điểm của ứng dụng của bạn

Bufferbloat can happen at every level in the stack where there is a buffer. Đối với mỗi kết nối, bên nhận chứa các bộ đệm này

  • bộ đệm hệ điều hành. tuning them is an advanced optimization
  • Bộ đệm byte StreamReader. giới hạn mặc định là 64 KiB. Bạn có thể đặt giới hạn khác bằng cách chuyển đối số từ khóa read_limit tới connect[] hoặc serve[]
  • Incoming messages deque. its size depends both on the size and the number of messages it contains. Theo mặc định, kích thước được mã hóa UTF-8 tối đa là 1 MiB và số lượng tối đa là 32. In the worst case, after UTF-8 decoding, a single message could take up to 4 MiB of memory and the overall memory consumption could reach 128 MiB. You should adjust these limits by setting the max_size and max_queue keyword arguments of connect[] or serve[] according to your application's requirements

Đối với mỗi kết nối, bên gửi chứa các bộ đệm này

  • Bộ đệm byte StreamWriter. the default size is 64 KiB. Bạn có thể đặt giới hạn khác bằng cách chuyển đối số từ khóa write_limit tới connect[] hoặc serve[]
  • bộ đệm hệ điều hành. tuning them is an advanced optimization

đồng thời

Awaiting any combination of recv[], send[], close[] ping[], or pong[] concurrently is safe, including multiple calls to the same method, with one exception and one limitation

  • Chỉ một coroutine có thể nhận tin nhắn tại một thời điểm. Ràng buộc này tránh hành vi không xác định [và đơn giản hóa việc thực hiện]. Nếu một coroutine đang đợi recv[], thì việc đợi nó một lần nữa trong một coroutine khác sẽ làm tăng RuntimeError
  • Gửi một tin nhắn bị phân mảnh buộc tuần tự hóa. Thật vậy, giao thức WebSocket không hỗ trợ ghép kênh tin nhắn. If a coroutine is awaiting send[] to send a fragmented message, awaiting it again in another coroutine waits until the first call completes. Điều này sẽ minh bạch trong nhiều trường hợp. It may be a concern if the fragmented message is generated slowly by an asynchronous iterator

Receiving frames is independent from sending frames. Điều này cô lập recv[], nhận khung, từ các phương thức khác, gửi khung

While the connection is open, each frame is sent with a single write. Kết hợp với mô hình đồng thời của asyncio, điều này thực thi tuần tự hóa. Yêu cầu duy nhất khác là ngăn chặn việc xen kẽ các khung dữ liệu khác ở giữa thông báo bị phân mảnh

Sau khi đóng kết nối, việc gửi một khung sẽ tăng ConnectionClosed, điều này an toàn

Memory usage

In most cases, memory usage of a WebSocket server is proportional to the number of open connections. Khi một máy chủ xử lý hàng nghìn kết nối, việc sử dụng bộ nhớ có thể trở thành nút cổ chai

Việc sử dụng bộ nhớ của một kết nối là tổng của

1. số lượng ổ cắm web bộ nhớ cơ sở yêu cầu cho mỗi kết nối,2. the amount of data held in buffers before the application processes it,3. any additional memory allocated by the application itself

đường cơ sở

Compression settings are the main factor affecting the baseline amount of memory used by each connection

With websockets' defaults, on the server side, a single connections uses 70 KiB of memory

Refer to the topic guide on compression to learn more about tuning compression settings

bộ đệm

Under normal circumstances, buffers are almost always empty

Trong điều kiện tải cao, nếu một máy chủ nhận được nhiều tin nhắn hơn mức có thể xử lý, thì lỗi tràn bộ nhớ đệm có thể dẫn đến việc sử dụng bộ nhớ quá mức

By default websockets has generous limits. It is strongly recommended to adapt them to your application. Khi bạn gọi phục vụ[]

  • Set max_size [default. 1 MiB, mã hóa UTF-8] thành kích thước tối đa của thông báo mà ứng dụng của bạn tạo ra
  • Set max_queue [default. 32] to the maximum number of messages your application expects to receive faster than it can process them. Hàng đợi cung cấp khả năng chịu cụm mà không làm chậm kết nối TCP

Furthermore, you can lower read_limit and write_limit [default. 64 KiB] để giảm kích thước bộ đệm cho dữ liệu đến và đi

The design document provides more details about buffers

Bảo vệ

mã hóa

For production use, a server should require encrypted connections

See this example of encrypting connections with TLS

Memory usage

WARNING

Kẻ tấn công có thể mở số lượng kết nối tùy ý sẽ có thể thực hiện tấn công từ chối dịch vụ do cạn kiệt bộ nhớ. Nếu lo ngại về các cuộc tấn công từ chối dịch vụ, bạn phải từ chối các kết nối đáng ngờ trước khi chúng tiếp cận ổ cắm web, thường là trong một proxy ngược

Với cài đặt mặc định, việc mở một kết nối sẽ sử dụng 70 KiB bộ nhớ

Việc gửi một số tin nhắn được nén ở mức độ cao có thể sử dụng tới 128 MiB bộ nhớ với hệ số khuếch đại là 1000 giữa lưu lượng truy cập mạng và mức sử dụng bộ nhớ

Định cấu hình máy chủ để tối ưu hóa việc sử dụng bộ nhớ sẽ cải thiện bảo mật bên cạnh việc cải thiện hiệu suất

Other limits

websockets thực hiện các giới hạn bổ sung về lượng dữ liệu mà nó chấp nhận để giảm thiểu khả năng tiếp xúc với các lỗ hổng bảo mật

In the opening handshake, websockets limits the number of HTTP headers to 256 and the size of an individual header to 4096 bytes. Các giới hạn này lớn hơn từ 10 đến 20 lần so với những gì được mong đợi trong các trường hợp sử dụng tiêu chuẩn. They're hard-coded

If you need to change these limits, you can monkey-patch the constants in websockets. http11

Performance

Here are tips to optimize performance

uvloop

You can make a websockets application faster by running it with uvloop

[Lời khuyên này không dành riêng cho websockets. It applies to any asyncio application. ]

broadcast

broadcast[] is the most efficient way to send a message to many clients

GIỚI THIỆU WEBSOCKET

This is about websockets-the-project rather than websockets-the-software

Changelog

Backwards-compatibility policy

websockets is intended for production use. Vì vậy, ổn định là mục tiêu

websockets also aims at providing the best API for WebSocket in Python

Trong khi chúng tôi coi trọng sự ổn định, chúng tôi coi trọng sự tiến bộ hơn. When an improvement requires changing a public API, we make the change and document it in this changelog

Khi có thể với nỗ lực hợp lý, chúng tôi duy trì khả năng tương thích ngược trong 5 năm sau khi phát hành giới thiệu thay đổi

Khi một bản phát hành chứa các thay đổi API không tương thích ngược, phiên bản chính sẽ tăng lên, nếu không thì phiên bản phụ sẽ tăng lên. Các phiên bản vá chỉ để khắc phục hồi quy ngay sau khi phát hành

Chỉ các API được ghi lại là công khai. API không có giấy tờ được coi là riêng tư. Họ có thể thay đổi bất cứ lúc nào

10. 4

Ngày 25 tháng 10 năm 2022

Các tính năng mới

  • Khả năng tương thích đã được xác thực với Python 3. 11
  • Đã thêm thuộc tính độ trễ vào các giao thức
  • Đã thay đổi ping để trả về độ trễ của kết nối
  • Được hỗ trợ ghi đè hoặc xóa tiêu đề Tác nhân người dùng trong máy khách và tiêu đề Máy chủ trong máy chủ
  • Đã thêm hướng dẫn triển khai cho nhiều Nền tảng hơn với tư cách là nhà cung cấp Dịch vụ

Improvements

•Improved FAQ

10. 3

17 Tháng Tư, 2022

Thay đổi ngược không tương thích

Thuộc tính ngoại lệ của Yêu cầu và Phản hồi không được dùng nữa

Thay vào đó, hãy sử dụng thuộc tính handshake_exc của ServerConnection và ClientConnection

Xem Tích hợp lớp Sans-I/O để biết chi tiết

Improvements

• Giảm nhiễu trong nhật ký khi ssl hoặc zlib tăng ngoại lệ

10. 2

Ngày 21 tháng 2 năm 2022

Improvements

  • Thực hiện đàm phán nén lỏng lẻo hơn để tương thích với Firefox
  • Cải thiện câu hỏi thường gặp và hướng dẫn bắt đầu nhanh

Sửa lỗi

  • Đã sửa lỗi không tương thích ngược trong 10. 1 cho trình xử lý kết nối được tạo bằng funcools. một phần[]
  • Tránh rò rỉ ổ cắm mở khi kết nối [] bị hủy

10. 1

Ngày 14 tháng 11 năm 2021

Các tính năng mới

  • Đã thêm hướng dẫn
  • Made the second parameter of connection handlers optional. Nó sẽ không được dùng nữa trong bản phát hành chính tiếp theo. Đường dẫn yêu cầu có sẵn trong thuộc tính đường dẫn của đối số đầu tiên

    Nếu bạn đã triển khai trình xử lý kết nối của máy chủ dưới dạng

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
85

Bạn nên thay thế nó bằng

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
86

• Đã thêm python -m websockets --version

Improvements

  • Đã thêm bánh xe cho Python 3. 10, PyPy 3. 7 và cho nhiều nền tảng hơn
  • Hoàn nguyên tối ưu hóa cài đặt nén mặc định cho máy khách, chủ yếu để tránh gây ra lỗi trong các máy chủ được triển khai kém như AWS API Gateway
  • Nhân bản toàn bộ Server API trong WebSocketServer
  • Cải thiện hiệu suất cho các tin nhắn lớn trên bộ xử lý ARM
  • Đã ghi lại cách tự động tải lại các thay đổi mã trong quá trình phát triển

Sửa lỗi

•Tránh các kết nối TCP đóng một nửa đã bị đóng

10. 0

Ngày 9 tháng 9 năm 2021

Thay đổi ngược không tương thích

ổ cắm web 10. 0 requires Python ≥ 3. 7

ổ cắm web 9. 1 là phiên bản cuối cùng hỗ trợ Python 3. 6

Tham số vòng lặp không được dùng trong tất cả các API

Điều này phản ánh một quyết định được đưa ra trong Python 3. 8. Xem ghi chú phát hành của Python 3. 10 để biết chi tiết

Tham số vòng lặp cũng bị xóa khỏi WebSocketServer. Điều này phải minh bạch

connect[] hết thời gian chờ sau 10 giây theo mặc định

Bạn có thể điều chỉnh thời gian chờ bằng tham số open_timeout. Đặt nó thành Không để tắt hoàn toàn thời gian chờ

Tùy chọn Legacy_recv không được dùng nữa

Xem ghi chú phát hành của websockets 3. 0 để biết chi tiết

Chữ ký của ConnectionClosed đã thay đổi

Nếu bạn tăng ConnectionClosed hoặc một lớp con, thay vì bắt chúng khi ổ cắm web tăng chúng, bạn phải thay đổi mã của mình

Một thông số msg đã được thêm vào UnlimitedURI

Nếu bạn tăng InvalidURI, thay vì bắt nó khi websockets tăng nó, bạn phải thay đổi mã của mình

Các tính năng mới

websockets 10. 0 giới thiệu API Sans-I/O để tích hợp dễ dàng hơn trong các thư viện của bên thứ ba. "

Nếu bạn đang tích hợp ổ cắm web trong thư viện, thay vì chỉ sử dụng nó, hãy xem hướng dẫn tích hợp Sans-I/O

  • Đã thêm khả năng tương thích với Python 3. 10
  • Đã thêm quảng bá [] để gửi tin nhắn cho nhiều khách hàng
  • Đã thêm hỗ trợ để tự động kết nối lại bằng cách sử dụng connect[] làm trình lặp không đồng bộ
  • Đã thêm open_timeout vào kết nối[]
  • Đã ghi lại cách tích hợp với Django
  • Đã ghi lại cách triển khai ổ cắm web trong sản xuất, với một số tùy chọn
  • Tài liệu làm thế nào để xác thực kết nối
  • Tài liệu làm thế nào để quảng bá tin nhắn đến nhiều kết nối

Improvements

  • Cải thiện ghi nhật ký. Xem hướng dẫn đăng nhập
  • Optimized default compression settings to reduce memory usage
  • Tối ưu hóa quá trình xử lý tin nhắn từ máy khách đến máy chủ khi tiện ích mở rộng C không khả dụng
  • Chuyển hướng tương đối được hỗ trợ trong connect[]
  • Kết nối TCP được xử lý giảm trong quá trình bắt tay mở
  • Made it easier to customize authentication with check_credentials[]
  • Cung cấp thông tin bổ sung trong trường hợp ngoại lệ ConnectionClosed
  • Đã làm rõ một số ngoại lệ hoặc thông điệp tường trình
  • tài liệu tái cấu trúc
  • Cải thiện tài liệu API
  • Câu hỏi thường gặp mở rộng

Sửa lỗi

•Tránh sự cố khi nhận lệnh ping trong khi kết nối đang đóng

9. 1

27 Tháng Năm, 2021

sửa lỗi bảo mật

ổ cắm web 9. 1 khắc phục sự cố bảo mật được giới thiệu trong 8. 0

Phiên bản 8. 0 dễ bị tấn công theo thời gian vào mật khẩu HTTP Basic Auth [CVE-2021-33880]

9. 0. 2

15 Tháng Năm, 2021

Sửa lỗi

  • Đã khôi phục khả năng tương thích của ổ cắm web python -m với Python Hello world! < Hello world! Connection closed: 1000 [OK].87

Các tính năng mới

•Added compatibility with Python 3. 9

Improvements

  • Đã thêm hỗ trợ cho IRI ngoài URI
  • Đã thêm mã đóng 1012, 1013 và 1014
  • Đã xảy ra lỗi khi chuyển lệnh tới send[]
  • Cải thiện báo cáo lỗi

Sửa lỗi

  • Đã sửa lỗi gửi tin nhắn bị phân mảnh, nén
  • Đã sửa lỗi tiêu đề Máy chủ được gửi khi kết nối với địa chỉ IPv6
  • Đã sửa lỗi tạo máy khách hoặc máy chủ với ổ cắm Unix hiện có
  • Kích thước cookie tối đa được căn chỉnh với các trình duyệt web phổ biến
  • Việc hủy được đảm bảo luôn lan truyền, ngay cả trên các phiên bản Python trong đó CancelledError kế thừa Ngoại lệ

8. 1

1 tháng 11, 2019

Các tính năng mới

•Thêm khả năng tương thích với Python 3. 8

8. 0. 2

31 Tháng Bảy, 2019

Sửa lỗi

  • Đã khôi phục khả năng truyền socket với tham số sock của serve[]
  • Đã xóa một xác nhận không chính xác khi kết nối bị rớt

8. 0. 1

21 Tháng Bảy, 2019

Sửa lỗi

• Đã khôi phục khả năng nhập WebSocketProtocolError từ websockets

8. 0

7 Tháng Bảy, 2019

Thay đổi ngược không tương thích

ổ cắm web 8. 0 requires Python ≥ 3. 6

ổ cắm web 7. 0 là phiên bản cuối cùng hỗ trợ Python 3. 4 và 3. 5

process_request hiện được mong đợi là một coroutine

Nếu bạn đang chuyển một đối số process_request tới serve[] hoặc WebSocketServerProtocol hoặc nếu bạn đang ghi đè process_request[] trong một lớp con, hãy xác định nó bằng async def thay vì def. Trước đây, cả hai đều được hỗ trợ

Để tương thích ngược, các chức năng vẫn được chấp nhận, nhưng chức năng kết hợp và coroutine sẽ không hoạt động trong một số trường hợp kế thừa

max_queue phải là Không để tắt giới hạn

Nếu bạn đang đặt max_queue=0 để làm cho hàng đợi thư đến không bị chặn, hãy đổi thành max_queue=None

Các thuộc tính máy chủ, cổng và bảo mật của WebSocketCommonProtocol không được dùng nữa. "

Sử dụng local_address trong máy chủ và remote_address trong máy khách thay vì máy chủ và cổng

WebSocketProtocolError được đổi tên thành ProtocolError. "

Một bí danh cung cấp khả năng tương thích ngược

read_response[] hiện trả về cụm từ lý do

Nếu bạn đang sử dụng API cấp thấp này, bạn phải thay đổi mã của mình

Các tính năng mới

  • Đã thêm basic_auth_protocol_factory[] để thực thi HTTP Basic Auth ở phía máy chủ
  • connect[] xử lý chuyển hướng từ máy chủ trong quá trình bắt tay
  • connect[] hỗ trợ ghi đè máy chủ và cổng
  • Added unix_connect[] for connecting to Unix sockets
  • Đã thêm hỗ trợ cho các trình tạo không đồng bộ trong send[] để tạo các tin nhắn bị phân mảnh tăng dần
  • Đã bật đường đọc trong ứng dụng khách tương tác
  • Đã thêm gợi ý loại [PEP 484]
  • Đã thêm Câu hỏi thường gặp vào tài liệu
  • Đã thêm tài liệu cho tiện ích mở rộng
  • Tài liệu làm thế nào để tối ưu hóa việc sử dụng bộ nhớ

Improvements

  • send [], ping [] và pong [] hỗ trợ các loại byte giống như bytearray và memoryview ngoài byte
  • Đã thêm các lớp con ConnectionClosedOK và ConnectionClosedError của ConnectionClosed để phân biệt việc chấm dứt kết nối thông thường do lỗi
  • Máy chủ WebSocket đã thay đổi. close[] để thực hiện bắt tay đóng đúng cách thay vì kết nối không thành công
  • Thông báo lỗi được cải thiện khi phân tích cú pháp HTTP không thành công
  • Cải thiện tài liệu API

Sửa lỗi

  • Đã ngăn chặn các thông báo tường trình giả mạo về các ngoại lệ ConnectionClosed trong tác vụ ping cố định. Nếu bạn đang sử dụng ping_timeout=None như một giải pháp thay thế, bạn có thể xóa nó
  • Đã tránh được sự cố khi một extra_headers có thể gọi trả về Không có

7. 0

1 tháng 11, 2018

Thay đổi ngược không tương thích

Keepalive được bật theo mặc định

ổ cắm web hiện gửi các khung Ping đều đặn và đóng kết nối nếu nó không nhận được khung Pong phù hợp. Xem WebSocketCommonProtocol để biết chi tiết

Termination of connections by WebSocketServer. đóng[] thay đổi

Trước đây, các trình xử lý kết nối đã bị hủy. Bây giờ, các kết nối đã bị đóng với mã đóng 1001 [sẽ biến mất]

Từ quan điểm của trình xử lý kết nối, điều này giống như khi điểm cuối từ xa bị ngắt kết nối. Điều này loại bỏ nhu cầu chuẩn bị cho CancelledError trong trình xử lý kết nối

Bạn có thể khôi phục hành vi trước đó bằng cách thêm dòng sau vào đầu trình xử lý kết nối

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
88

Gọi recv[] đồng thời làm tăng RuntimeError. "

Concurrent calls lead to non-deterministic behavior because there are no guarantees about which coroutine will receive which message

The timeout argument of serve[]and connect[] is renamed to close_timeout . "

This prevents confusion with ping_timeout

Để tương thích ngược, thời gian chờ vẫn được hỗ trợ

The origins argument of serve[] changes

Bao gồm Không có trong danh sách thay vì '' để cho phép các yêu cầu không chứa tiêu đề Xuất xứ

Pending pings aren't canceled when the connection is closed

A ping — as in ping = await websocket. ping[] — for which no pong was received yet used to be canceled when the connection is closed, so that await ping raised CancelledError

Now await ping raises ConnectionClosed like other public APIs

Các tính năng mới

  • Added process_request and select_subprotocol arguments to serve[] and WebSocketServerProtocol to facilitate customization of process_request[] and select_subprotocol[]
  • Đã thêm hỗ trợ để gửi tin nhắn bị phân mảnh
  • Đã thêm phương thức wait_close[] vào các giao thức
  • Added an interactive client: python -m websockets .

Improvements

  • Cải thiện việc xử lý nhiều tiêu đề HTTP có cùng tên
  • Improved error messages when a required HTTP header is missing

Sửa lỗi

•Sửa lỗi mất dữ liệu trong recv[]. hủy nó không đúng lúc có thể dẫn đến việc tin nhắn bị hủy

6. 0

16 Tháng Bảy, 2018

Thay đổi ngược không tương thích

The Headers class is introduced andseveral APIs are updated to use it. "

  • The request_headers argument of process_request[] is now a Headers instead of an http. khách hàng. HTTPMessage
  • The request_headers and response_headers attributes of WebSocketCommonProtocol are now Headers instead of http. client. HTTPMessage
  • The raw_request_headers and raw_response_headers attributes of WebSocketCommonProtocol are removed. Use raw_items[] instead
  • Các chức năng được xác định trong mô-đun bắt tay hiện nhận Tiêu đề trong đối số thay vì các hàm get_header hoặc set_header. This affects libraries that rely on low-level APIs
  • Functions defined in the http module now return HTTP headers as Headers instead of lists of [name, value] pairs

Since Headers and http. khách hàng. HTTPMessage provide similar APIs, much of the code dealing with HTTP headers won't require changes

Các tính năng mới

•Thêm khả năng tương thích với Python 3. 7

5. 0. 1

24 Tháng Năm, 2018

Sửa lỗi

• Đã sửa hồi quy trong 5. 0 đã phá vỡ một số lời gọi của phục vụ [] và kết nối []

5. 0

22 Tháng Năm, 2018

sửa lỗi bảo mật

ổ cắm web 5. 0 khắc phục sự cố bảo mật được giới thiệu trong 4. 0

Phiên bản 4. 0 dễ bị từ chối dịch vụ do cạn kiệt bộ nhớ vì nó không thực thi max_size khi giải nén thư đã nén [CVE-2018-1000518]

Thay đổi ngược không tương thích

Trường user_info được thêm vào giá trị trả về củaparse_uri và WebSocketURI. "

Nếu bạn đang giải nén WebSocketURI thành bốn biến, hãy điều chỉnh mã của bạn để giải thích cho trường thứ năm đó

Các tính năng mới

  • connect[] thực hiện HTTP Basic Auth khi URI chứa thông tin xác thực
  • unix_serve[] có thể được sử dụng làm trình quản lý bối cảnh không đồng bộ trên Python ≥ 3. 5. 1
  • Added the closed property to protocols
  • Đã thêm các ví dụ mới trong tài liệu

Improvements

  • Việc lặp lại các tin nhắn đến không còn gây ra ngoại lệ khi kết nối kết thúc với mã đóng 1001 [sẽ biến mất]
  • Yêu cầu HTTP đơn giản hiện nhận được phản hồi Yêu cầu nâng cấp 426 và không ghi lại dấu vết ngăn xếp
  • Nếu ping[] không nhận được pong, nó sẽ bị hủy khi kết nối bị đóng
  • Đã báo cáo nguyên nhân của ngoại lệ ConnectionClosed
  • Đã dừng dấu vết ngăn xếp nhật ký khi kết nối TCP chết sớm
  • Đã ngăn ghi vào kết nối TCP đang đóng trong khi tắt máy không sạch
  • Làm cho việc chấm dứt kết nối mạnh mẽ hơn đối với tắc nghẽn mạng
  • Đã ngăn xử lý các khung đến sau khi không kết nối được
  • Cập nhật tài liệu với các tính năng mới từ Python 3. 6
  • Cải thiện một số phần của tài liệu

Sửa lỗi

  • TypeError bị chặn do thiếu mã đóng khi đóng kết nối
  • Đã sửa lỗi điều kiện cuộc đua trong lần bắt tay kết thúc làm tăng Trạng thái không hợp lệ

4. 0. 1

2 Tháng Mười Một, 2017

Sửa lỗi

• Đã khắc phục sự cố với bao bì của 4. 0 phát hành

4. 0

2 Tháng Mười Một, 2017

Thay đổi ngược không tương thích

ổ cắm web 4. 0 yêu cầu Python ≥ 3. 4

websockets 3. 4 is the last version supporting Python 3. 3

Compression is enabled by default

Vào tháng 8 năm 2017, Firefox và Chrome hỗ trợ tiện ích mở rộng permessage-deflate, nhưng Safari và IE thì không

Nén sẽ cải thiện hiệu suất nhưng nó làm tăng mức sử dụng RAM và CPU

Nếu bạn muốn tắt tính năng nén, hãy thêm nén=None khi gọi hàm phục vụ[] hoặc kết nối[]

Thuộc tính state_name của các giao thức không được dùng nữa

sử dụng giao thức. tiểu bang. tên thay vì giao thức. tên nhà nước

Các tính năng mới

  • Các phiên bản WebSocketCommonProtocol có thể được sử dụng làm trình lặp không đồng bộ trên Python ≥ 3. 6. Họ mang lại tin nhắn đến
  • Đã thêm unix_serve[] để nghe trên các ổ cắm Unix
  • Đã thêm thuộc tính sockets vào giá trị trả về của serve[]
  • Cho phép extra_headers ghi đè tiêu đề Máy chủ và Tác nhân người dùng

Improvements

  • Tài liệu được tổ chức lại và mở rộng
  • Viết lại việc chấm dứt kết nối để tăng độ bền trong các trường hợp cạnh
  • Giảm mức độ chi tiết của nhật ký "Không kết nối WebSocket"

Sửa lỗi

  • Đã hủy kết nối nếu chúng không đóng trong thời gian chờ đã định cấu hình
  • Stopped leaking pending tasks when cancel[] is called on a connection while it's being closed

3. 4

20 Tháng Tám, 2017

Thay đổi ngược không tương thích

Trạng thái không hợp lệ được thay thế bằng Mã trạng thái không hợp lệ. "

Ngoại lệ này được đưa ra khi connect[] nhận được mã trạng thái phản hồi không hợp lệ từ máy chủ

Các tính năng mới

  • serve[] có thể được sử dụng làm trình quản lý ngữ cảnh không đồng bộ trên Python ≥ 3. 5. 1
  • Đã thêm hỗ trợ để tùy chỉnh xử lý các kết nối đến với process_request[]
  • Có thể định cấu hình kích thước bộ đệm đọc và ghi

Improvements

  • Đã đổi tên đối số klass của serve[] và connect[] thành create_protocol để phản ánh rằng nó cũng có thể gọi được. Để tương thích ngược, klass vẫn được hỗ trợ
  • Viết lại xử lý HTTP để đơn giản và hiệu suất
  • Đã thêm tiện ích mở rộng C tùy chọn để tăng tốc các hoạt động cấp thấp

Sửa lỗi

• Cung cấp đối số sock để connect[] không còn bị treo

3. 3

29 Tháng Ba, 2017

Các tính năng mới

• Đảm bảo khả năng tương thích với Python 3. 6

Improvements

•Giảm tiếng ồn trong nhật ký do đặt lại kết nối

Sửa lỗi

•Tránh sự cố khi ghi đồng thời trên các kết nối chậm

3. 2

17 Tháng Tám, 2016

Các tính năng mới

• Đã thêm các đối số thời gian chờ, max_size và max_queue để kết nối[] và phục vụ[]

Improvements

•Làm cho việc tắt máy chủ trở nên mạnh mẽ hơn

3. 1

April 21, 2016

Các tính năng mới

•Thêm điều khiển luồng cho dữ liệu đến

Sửa lỗi

•Tránh cảnh báo khi đóng kết nối trước khi bắt tay mở

3. 0

25 Tháng Mười Hai, 2015

Thay đổi ngược không tương thích

recv[] hiện tăng ngoại lệ khi kết nối bị đóng. "

recv[] được sử dụng để trả về Không khi kết nối bị đóng. Điều này yêu cầu kiểm tra giá trị trả về của mọi cuộc gọi

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
89

Bây giờ, thay vào đó, nó tạo ra một ngoại lệ ConnectionClosed. Đây là Pythonic hơn. Mã trước đó có thể được đơn giản hóa thành

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
90

Khi triển khai máy chủ, không có lý do chính đáng nào để xử lý các trường hợp ngoại lệ đó. Hãy để chúng nổi lên, chấm dứt quy trình xử lý và máy chủ sẽ đơn giản bỏ qua chúng

Để tránh các dự án bị mắc kẹt được xây dựng trên phiên bản cũ hơn, hành vi trước đó có thể được khôi phục bằng cách chuyển Legacy_recv=True tới serve[], connect[], WebSocketServerProtocol hoặc WebSocketClientProtocol

Các tính năng mới

  • connect[] có thể được sử dụng làm trình quản lý ngữ cảnh không đồng bộ trên Python ≥ 3. 5. 1
  • ping[] và pong[] hỗ trợ dữ liệu được truyền dưới dạng str ngoài byte
  • Tạo thuộc tính state_name trên các giao thức thành API công khai

Improvements

  • Tài liệu được cập nhật với cú pháp chờ đợi và không đồng bộ từ Python 3. 5
  • Worked around an asyncio bug affecting connection termination under load
  • tài liệu cải tiến

2. 7

18 Tháng mười một 2015

Các tính năng mới

•Thêm khả năng tương thích với Python 3. 5

Improvements

•Làm mới tài liệu

2. 6

18 Tháng Tám, 2015

Các tính năng mới

  • Đã thêm thuộc tính local_address và remote_address trên các giao thức
  • Đã đóng các kết nối đang mở với mã 1001 khi máy chủ tắt

Sửa lỗi

•Tránh phân mảnh TCP của các khung nhỏ

2. 5

28 Tháng Bảy, 2015

Các tính năng mới

  • Cung cấp quyền truy cập vào yêu cầu bắt tay và tiêu đề HTTP phản hồi
  • Được phép tùy chỉnh các tiêu đề HTTP phản hồi và yêu cầu bắt tay
  • Đã thêm hỗ trợ để chạy trên vòng lặp sự kiện không mặc định

Improvements

  • tài liệu cải tiến
  • Đã gửi mã trạng thái 403 thay vì 400 khi yêu cầu Xuất xứ không được phép
  • Làm rõ rằng bắt tay đóng có thể được bắt đầu bởi khách hàng
  • Đặt mã đóng và lý do nhất quán hơn
  • Tăng cường chấm dứt kết nối

Sửa lỗi

• Hủy recv[] không còn làm rớt tin nhắn tiếp theo

2. 4

31 Tháng Một, 2015

Các tính năng mới

  • Added support for subprotocols
  • Đã thêm đối số vòng lặp để kết nối [] và phục vụ []

2. 3

3 Tháng mười một 2014

Improvements

• Cải thiện việc tuân thủ các mã đóng

2. 2

28 Tháng Bảy, 2014

Các tính năng mới

•Thêm hỗ trợ giới hạn kích thước thư

2. 1

26 Tháng Tư, 2014

Các tính năng mới

  • Đã thêm thuộc tính máy chủ, cổng và bảo mật trên các giao thức
  • Đã thêm hỗ trợ để cung cấp và kiểm tra Xuất xứ

2. 0

16 Tháng hai, 2014

Thay đổi ngược không tương thích

send[],ping[], và pong[] hiện là coroutine. "

Chúng từng là các chức năng

Thay vì

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
91

bạn phải viết

$ python -m websockets ws://localhost:8765/
Connected to ws://localhost:8765/.
> Hello world!
< Hello world!
Connection closed: 1000 [OK].
92

Các tính năng mới

•Thêm điều khiển luồng cho dữ liệu gửi đi

1. 0

14 Tháng mười một 2013

Các tính năng mới

•Phát hành lần đầu ra công chúng

Contributing

Cảm ơn bạn đã dành thời gian đóng góp cho websockets

quy tắc ứng xử

Dự án này và mọi người tham gia dự án được điều chỉnh bởi Bộ quy tắc ứng xử. Bằng cách tham gia, bạn phải duy trì mã này. Vui lòng báo cáo hành vi không phù hợp với aymeric DOT augustin AT fractalideas DOT com

[Nếu tôi là người có hành vi không phù hợp, xin vui lòng chấp nhận lời xin lỗi của tôi. Tôi biết tôi có thể gây rối. Tôi không thể mong đợi bạn nói với tôi, nhưng nếu bạn chọn làm như vậy, tôi sẽ cố gắng hết sức để xử lý những lời chỉ trích một cách xây dựng. -- Aymeric]

Đóng góp

Báo cáo lỗi, bản vá và đề xuất được chào đón

Vui lòng mở một vấn đề hoặc gửi yêu cầu kéo

Phản hồi về tài liệu đặc biệt có giá trị vì tác giả chính cảm thấy tự tin hơn khi viết mã hơn là viết tài liệu. -]

Nếu bạn đang thắc mắc tại sao mọi thứ được thực hiện theo một cách nhất định, thì tài liệu thiết kế cung cấp nhiều chi tiết về phần bên trong của ổ cắm web

câu hỏi

Các vấn đề về GitHub không phải là phương tiện tốt để xử lý các câu hỏi. Có những nơi tốt hơn để đặt câu hỏi, ví dụ như Stack Overflow

Nếu bạn vẫn muốn đặt câu hỏi, vui lòng đảm bảo rằng

  • it's a question about websockets and not about asyncio;
  • nó không được trả lời trong tài liệu;
  • nó chưa được hỏi

Một câu hỏi hay có thể được viết như một gợi ý để cải thiện tài liệu

người dùng tiền điện tử

ổ cắm web dường như khá phổ biến để giao tiếp với Bitcoin hoặc các trình theo dõi tiền điện tử khác. Tôi cực lực phản đối dấu chân carbon của Bitcoin

Tôi biết về những nỗ lực xây dựng mô hình bằng chứng cổ phần. Tôi sẽ quan tâm khi tổng mức tiêu thụ năng lượng của tất cả các loại tiền điện tử giảm xuống mức không nhảm nhí

Bạn đã phủ nhận tất cả những nỗ lực của nhân loại để phát triển năng lượng tái tạo. Xin hãy ngừng sưởi ấm hành tinh nơi các con tôi sẽ phải sinh sống

Vì websockets được phát hành theo giấy phép nguồn mở nên bạn có thể sử dụng nó cho bất kỳ mục đích nào bạn muốn. Tuy nhiên, tôi sẽ không dành bất kỳ thời gian nào của mình để giúp bạn

Tôi sẽ đóng tổng thể các vấn đề liên quan đến Bitcoin hoặc tiền điện tử dưới bất kỳ hình thức nào

Giấy phép

Bản quyền [c] 2013-2021 Aymeric Augustin và những người đóng góp. Đã đăng ký Bản quyền

Việc phân phối lại và sử dụng ở dạng nguồn và dạng nhị phân, có hoặc không có sửa đổi, được cho phép với điều kiện đáp ứng các điều kiện sau

  • Việc phân phối lại mã nguồn phải giữ lại thông báo bản quyền ở trên, danh sách các điều kiện này và tuyên bố từ chối trách nhiệm sau đây
  • 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
  • Không được sử dụng tên của ổ cắm web cũng như tên của những người đóng góp để xác nhận hoặc quảng cáo các sản phẩm có nguồn gốc từ phần mềm này mà không có sự cho phép cụ thể trước bằng văn bản

PHẦN MỀM NÀY ĐƯỢC CUNG CẤP BỞI NHỮNG NGƯỜI GIỮ BẢN QUYỀN VÀ CÁC ĐỐI TÁC "NGUYÊN TRẠNG" VÀ BẤT KỲ BẢO ĐẢM NÀO RÕ RÀNG HAY NGỤ Ý, BAO GỒM NHƯNG KHÔNG GIỚI HẠN Ở CÁC BẢO ĐẢM NGỤ Ý VỀ KHẢ NĂNG BÁN VÀ TÍNH PHÙ HỢP CHO MỘT MỤC ĐÍCH CỤ THỂ ĐỀU KHÔNG ĐƯỢC TỪ CHỐI. TRONG MỌI TRƯỜNG HỢP NGƯỜI GIỮ BẢN QUYỀN HOẶC NGƯỜI CỘNG TÁC SẼ KHÔNG CHỊU TRÁCH NHIỆM PHÁP LÝ VỀ BẤT KỲ THIỆT HẠI TRỰC TIẾP, GIÁN TIẾP, NGẪU NHIÊN, ĐẶC BIỆT, ĐIỂN HÌNH HOẶC DO HẬU QUẢ [BAO GỒM NHƯNG KHÔNG GIỚI HẠN Ở VIỆC MUA HÀNG HÓA HOẶC DỊCH VỤ THAY THẾ; MẤT SỬ DỤNG, DỮ LIỆU HOẶC LỢI NHUẬN;

ổ cắm web cho doanh nghiệp

Có sẵn như là một phần của Đăng ký Tidelift

[hình ảnh]

Tidelift đang làm việc với những người bảo trì ổ cắm web và hàng nghìn dự án nguồn mở khác để cung cấp hỗ trợ thương mại và bảo trì cho các phần phụ thuộc nguồn mở mà bạn sử dụng để xây dựng ứng dụng của mình. Tiết kiệm thời gian, giảm rủi ro và cải thiện tình trạng của mã, đồng thời trả tiền cho người duy trì chính xác các phụ thuộc mà bạn sử dụng

Phần mềm nguồn mở sẵn sàng cho doanh nghiệp—được quản lý cho bạn

Đăng ký Tidelift là đăng ký nguồn mở được quản lý cho các phần phụ thuộc của ứng dụng bao gồm hàng triệu dự án nguồn mở trên JavaScript, Python, Java, PHP, Ruby,. NETvà hơn thế nữa

Đăng ký của bạn bao gồm

•Cập nhật bảo mật

• Nhóm phản hồi bảo mật của Tidelift điều phối các bản vá cho các lỗ hổng bảo mật mới vi phạm và cảnh báo ngay lập tức thông qua một kênh riêng, vì vậy chuỗi cung ứng phần mềm của bạn luôn được bảo mật

•Xác minh giấy phép và bồi thường

•Tidelift xác minh thông tin giấy phép để cho phép thực thi chính sách dễ dàng và bổ sung bồi thường tài sản trí tuệ để bảo vệ người sáng tạo và người dùng trong trường hợp xảy ra sự cố. Bạn luôn có hóa đơn tài liệu cập nhật 100% cho những người phụ thuộc của mình để chia sẻ với nhóm pháp lý, khách hàng hoặc đối tác của bạn

• Bảo trì và cải tiến code

•Tidelift ensures the software you rely on keeps working as long as you need it to work. Các phần phụ thuộc được quản lý của bạn được duy trì tích cực và chúng tôi tuyển thêm người bảo trì nếu cần

• Lựa chọn gói và hướng dẫn phiên bản

• Chúng tôi giúp bạn chọn các gói mã nguồn mở tốt nhất ngay từ đầu—và sau đó hướng dẫn bạn về các bản cập nhật để duy trì các bản phát hành tốt nhất khi có vấn đề mới phát sinh

•Đầu vào lộ trình

•Ngồi cùng bàn với những người tạo ra phần mềm bạn sử dụng. Những người bảo trì tham gia của Tidelift kiếm được nhiều thu nhập hơn khi phần mềm của họ được nhiều người đăng ký sử dụng hơn, vì vậy họ muốn biết bạn cần gì

• Tích hợp công cụ và đám mây

•Tidelift hoạt động với GitHub, GitLab, BitBucket, v.v. Chúng tôi hỗ trợ mọi nền tảng đám mây [và cả các mục tiêu triển khai khác]

Kết quả cuối cùng? . Điều đó có nghĩa là ít thời gian hơn để vật lộn với những câu đố bí truyền về mã nguồn mở và có nhiều thời gian hơn để xây dựng các ứng dụng của riêng bạn—và doanh nghiệp của bạn

Python kết nối với WebSockets như thế nào?

Ứng dụng khách WebSocket với Python . py” và nhập các gói như chúng tôi đã làm trong mã máy chủ của mình. Create a new File “client.py” and import the packages as we did in our server code. Bây giờ, hãy tạo một hàm bất đồng bộ Python [còn gọi là coroutine]. kiểm tra xác định không đồng bộ []. Chúng tôi sẽ sử dụng chức năng kết nối từ mô-đun WebSockets để xây dựng kết nối máy khách WebSocket.

Does Python support WebSockets?

websockets là thư viện để xây dựng máy chủ và máy khách WebSocket bằng Python tập trung vào tính chính xác, đơn giản, mạnh mẽ và hiệu suất. Được xây dựng dựa trên asyncio , khung I/O không đồng bộ tiêu chuẩn của Python, nó cung cấp một API dựa trên coroutine tao nhã.

Chủ Đề