server.rst (11182B)
1 Server 2 ====== 3 4 .. currentmodule:: websockets 5 6 Why does the server close the connection prematurely? 7 ----------------------------------------------------- 8 9 Your connection handler exits prematurely. Wait for the work to be finished 10 before returning. 11 12 For example, if your handler has a structure similar to:: 13 14 async def handler(websocket): 15 asyncio.create_task(do_some_work()) 16 17 change it to:: 18 19 async def handler(websocket): 20 await do_some_work() 21 22 Why does the server close the connection after one message? 23 ----------------------------------------------------------- 24 25 Your connection handler exits after processing one message. Write a loop to 26 process multiple messages. 27 28 For example, if your handler looks like this:: 29 30 async def handler(websocket): 31 print(websocket.recv()) 32 33 change it like this:: 34 35 async def handler(websocket): 36 async for message in websocket: 37 print(message) 38 39 *Don't feel bad if this happens to you — it's the most common question in 40 websockets' issue tracker :-)* 41 42 Why can only one client connect at a time? 43 ------------------------------------------ 44 45 Your connection handler blocks the event loop. Look for blocking calls. 46 47 Any call that may take some time must be asynchronous. 48 49 For example, this connection handler prevents the event loop from running during 50 one second:: 51 52 async def handler(websocket): 53 time.sleep(1) 54 ... 55 56 Change it to:: 57 58 async def handler(websocket): 59 await asyncio.sleep(1) 60 ... 61 62 In addition, calling a coroutine doesn't guarantee that it will yield control to 63 the event loop. 64 65 For example, this connection handler blocks the event loop by sending messages 66 continuously:: 67 68 async def handler(websocket): 69 while True: 70 await websocket.send("firehose!") 71 72 :meth:`~legacy.protocol.WebSocketCommonProtocol.send` completes synchronously as 73 long as there's space in send buffers. The event loop never runs. (This pattern 74 is uncommon in real-world applications. It occurs mostly in toy programs.) 75 76 You can avoid the issue by yielding control to the event loop explicitly:: 77 78 async def handler(websocket): 79 while True: 80 await websocket.send("firehose!") 81 await asyncio.sleep(0) 82 83 All this is part of learning asyncio. It isn't specific to websockets. 84 85 See also Python's documentation about `running blocking code`_. 86 87 .. _running blocking code: https://docs.python.org/3/library/asyncio-dev.html#running-blocking-code 88 89 .. _send-message-to-all-users: 90 91 How do I send a message to all users? 92 ------------------------------------- 93 94 Record all connections in a global variable:: 95 96 CONNECTIONS = set() 97 98 async def handler(websocket): 99 CONNECTIONS.add(websocket) 100 try: 101 await websocket.wait_closed() 102 finally: 103 CONNECTIONS.remove(websocket) 104 105 Then, call :func:`~websockets.broadcast`:: 106 107 import websockets 108 109 def message_all(message): 110 websockets.broadcast(CONNECTIONS, message) 111 112 If you're running multiple server processes, make sure you call ``message_all`` 113 in each process. 114 115 .. _send-message-to-single-user: 116 117 How do I send a message to a single user? 118 ----------------------------------------- 119 120 Record connections in a global variable, keyed by user identifier:: 121 122 CONNECTIONS = {} 123 124 async def handler(websocket): 125 user_id = ... # identify user in your app's context 126 CONNECTIONS[user_id] = websocket 127 try: 128 await websocket.wait_closed() 129 finally: 130 del CONNECTIONS[user_id] 131 132 Then, call :meth:`~legacy.protocol.WebSocketCommonProtocol.send`:: 133 134 async def message_user(user_id, message): 135 websocket = CONNECTIONS[user_id] # raises KeyError if user disconnected 136 await websocket.send(message) # may raise websockets.ConnectionClosed 137 138 Add error handling according to the behavior you want if the user disconnected 139 before the message could be sent. 140 141 This example supports only one connection per user. To support concurrent 142 connections by the same user, you can change ``CONNECTIONS`` to store a set of 143 connections for each user. 144 145 If you're running multiple server processes, call ``message_user`` in each 146 process. The process managing the user's connection sends the message; other 147 processes do nothing. 148 149 When you reach a scale where server processes cannot keep up with the stream of 150 all messages, you need a better architecture. For example, you could deploy an 151 external publish / subscribe system such as Redis_. Server processes would 152 subscribe their clients. Then, they would receive messages only for the 153 connections that they're managing. 154 155 .. _Redis: https://redis.io/ 156 157 How do I send a message to a channel, a topic, or some users? 158 ------------------------------------------------------------- 159 160 websockets doesn't provide built-in publish / subscribe functionality. 161 162 Record connections in a global variable, keyed by user identifier, as shown in 163 :ref:`How do I send a message to a single user?<send-message-to-single-user>` 164 165 Then, build the set of recipients and broadcast the message to them, as shown in 166 :ref:`How do I send a message to all users?<send-message-to-all-users>` 167 168 :doc:`../howto/django` contains a complete implementation of this pattern. 169 170 Again, as you scale, you may reach the performance limits of a basic in-process 171 implementation. You may need an external publish / subscribe system like Redis_. 172 173 .. _Redis: https://redis.io/ 174 175 How do I pass arguments to the connection handler? 176 -------------------------------------------------- 177 178 You can bind additional arguments to the connection handler with 179 :func:`functools.partial`:: 180 181 import asyncio 182 import functools 183 import websockets 184 185 async def handler(websocket, extra_argument): 186 ... 187 188 bound_handler = functools.partial(handler, extra_argument=42) 189 start_server = websockets.serve(bound_handler, ...) 190 191 Another way to achieve this result is to define the ``handler`` coroutine in 192 a scope where the ``extra_argument`` variable exists instead of injecting it 193 through an argument. 194 195 How do I access the request path? 196 --------------------------------- 197 198 It is available in the :attr:`~server.WebSocketServerProtocol.path` attribute. 199 200 You may route a connection to different handlers depending on the request path:: 201 202 async def handler(websocket): 203 if websocket.path == "/blue": 204 await blue_handler(websocket) 205 elif websocket.path == "/green": 206 await green_handler(websocket) 207 else: 208 # No handler for this path; close the connection. 209 return 210 211 You may also route the connection based on the first message received from the 212 client, as shown in the :doc:`tutorial <../intro/tutorial2>`. When you want to 213 authenticate the connection before routing it, this is usually more convenient. 214 215 Generally speaking, there is far less emphasis on the request path in WebSocket 216 servers than in HTTP servers. When a WebSocket server provides a single endpoint, 217 it may ignore the request path entirely. 218 219 How do I access HTTP headers? 220 ----------------------------- 221 222 To access HTTP headers during the WebSocket handshake, you can override 223 :attr:`~server.WebSocketServerProtocol.process_request`:: 224 225 async def process_request(self, path, request_headers): 226 authorization = request_headers["Authorization"] 227 228 Once the connection is established, HTTP headers are available in 229 :attr:`~server.WebSocketServerProtocol.request_headers` and 230 :attr:`~server.WebSocketServerProtocol.response_headers`:: 231 232 async def handler(websocket): 233 authorization = websocket.request_headers["Authorization"] 234 235 How do I set HTTP headers? 236 -------------------------- 237 238 To set the ``Sec-WebSocket-Extensions`` or ``Sec-WebSocket-Protocol`` headers in 239 the WebSocket handshake response, use the ``extensions`` or ``subprotocols`` 240 arguments of :func:`~server.serve`. 241 242 To override the ``Server`` header, use the ``server_header`` argument. Set it to 243 :obj:`None` to remove the header. 244 245 To set other HTTP headers, use the ``extra_headers`` argument. 246 247 How do I get the IP address of the client? 248 ------------------------------------------ 249 250 It's available in :attr:`~legacy.protocol.WebSocketCommonProtocol.remote_address`:: 251 252 async def handler(websocket): 253 remote_ip = websocket.remote_address[0] 254 255 How do I set the IP addresses that my server listens on? 256 -------------------------------------------------------- 257 258 Use the ``host`` argument of :meth:`~asyncio.loop.create_server`:: 259 260 await websockets.serve(handler, host="192.168.0.1", port=8080) 261 262 :func:`~server.serve` accepts the same arguments as 263 :meth:`~asyncio.loop.create_server`. 264 265 What does ``OSError: [Errno 99] error while attempting to bind on address ('::1', 80, 0, 0): address not available`` mean? 266 -------------------------------------------------------------------------------------------------------------------------- 267 268 You are calling :func:`~server.serve` without a ``host`` argument in a context 269 where IPv6 isn't available. 270 271 To listen only on IPv4, specify ``host="0.0.0.0"`` or ``family=socket.AF_INET``. 272 273 Refer to the documentation of :meth:`~asyncio.loop.create_server` for details. 274 275 How do I close a connection? 276 ---------------------------- 277 278 websockets takes care of closing the connection when the handler exits. 279 280 How do I stop a server? 281 ----------------------- 282 283 Exit the :func:`~server.serve` context manager. 284 285 Here's an example that terminates cleanly when it receives SIGTERM on Unix: 286 287 .. literalinclude:: ../../example/faq/shutdown_server.py 288 :emphasize-lines: 12-15,18 289 290 How do I stop a server while keeping existing connections open? 291 --------------------------------------------------------------- 292 293 Call the server's :meth:`~server.WebSocketServer.close` method with 294 ``close_connections=False``. 295 296 Here's how to adapt the example just above:: 297 298 async def server(): 299 ... 300 301 server = await websockets.serve(echo, "localhost", 8765) 302 await stop 303 await server.close(close_connections=False) 304 305 How do I implement a health check? 306 ---------------------------------- 307 308 Intercept WebSocket handshake requests with the 309 :meth:`~server.WebSocketServerProtocol.process_request` hook. 310 311 When a request is sent to the health check endpoint, treat is as an HTTP request 312 and return a ``(status, headers, body)`` tuple, as in this example: 313 314 .. literalinclude:: ../../example/faq/health_check_server.py 315 :emphasize-lines: 7-9,18 316 317 How do I run HTTP and WebSocket servers on the same port? 318 --------------------------------------------------------- 319 320 You don't. 321 322 HTTP and WebSocket have widely different operational characteristics. Running 323 them with the same server becomes inconvenient when you scale. 324 325 Providing an HTTP server is out of scope for websockets. It only aims at 326 providing a WebSocket server. 327 328 There's limited support for returning HTTP responses with the 329 :attr:`~server.WebSocketServerProtocol.process_request` hook. 330 331 If you need more, pick an HTTP server and run it separately. 332 333 Alternatively, pick an HTTP framework that builds on top of ``websockets`` to 334 support WebSocket connections, like Sanic_. 335 336 .. _Sanic: https://sanicframework.org/en/