@@ -78,6 +78,67 @@ trio.run(main)
78
78
A longer example is in ` examples/server.py ` . ** See the note above about using
79
79
SSL with the example client.**
80
80
81
+ ## Heartbeat recipe
82
+
83
+ If you wish to keep a connection open for long periods of time but do not need
84
+ to send messages frequently, then a heartbeat holds the connection open and also
85
+ detects when the connection drops unexpectedly. The following recipe
86
+ demonstrates how to implement a connection heartbeat using WebSocket's ping/pong
87
+ feature.
88
+
89
+ ``` python
90
+ async def heartbeat (ws , timeout , interval ):
91
+ '''
92
+ Send periodic pings on WebSocket ``ws``. Wait up to ``timeout`` seconds to
93
+ receive a pong before raising an exception. If a pong is received, then wait
94
+ ``interval`` seconds before sending the next ping.
95
+ '''
96
+ while True :
97
+ with trio.fail_after(timeout):
98
+ await ws.ping()
99
+ await trio.sleep(interval)
100
+
101
+ async def main ():
102
+ async with open_websocket_url(' ws://localhost/foo' ) as ws:
103
+ async with trio.open_nursery() as nursery:
104
+ nursery.start_soon(heartbeat, ws, 5 , 1 )
105
+ # Your application code goes here:
106
+ pass
107
+
108
+ trio.run(main)
109
+ ```
110
+
111
+ Note that the ` ping() ` method waits until it receives a pong frame, so it
112
+ ensures that the remote endpoint is still responsive. If the connection is
113
+ dropped unexpectedly or takes too long to respond, then ` heartbeat() ` will raise
114
+ an exception that will cancel the nursery. You may wish to implement additional
115
+ logic to automatically reconnect.
116
+
117
+ A heartbeat feature can be enabled in the example client with the
118
+ `` --heartbeat `` flag.
119
+
120
+ ** Note that the WebSocket RFC does not require a WebSocket to send a pong for each
121
+ ping:**
122
+
123
+ > If an endpoint receives a Ping frame and has not yet sent Pong frame(s) in
124
+ > response to previous Ping frame(s), the endpoint MAY elect to send a Pong
125
+ > frame for only the most recently processed Ping frame.
126
+
127
+ Therefore, if you have multiple pings in flight at the same time, you may not
128
+ get an equal number of pongs in response. The simplest strategy for dealing with
129
+ this is to only have one ping in flight at a time, as seen in the example above.
130
+ As an alternative, you can send a ` bytes ` payload with each ping. The server
131
+ will return the payload with the pong:
132
+
133
+ ``` python
134
+ await ws.ping(b ' my payload' )
135
+ pong == await ws.wait_pong()
136
+ assert pong == b ' my payload'
137
+ ```
138
+
139
+ You may want to embed a nonce or counter in the payload in order to correlate
140
+ pong events to the pings you have sent.
141
+
81
142
## Unit Tests
82
143
83
144
Unit tests are written in the pytest style. You must install the development
0 commit comments