-
-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
client example not handling closed connection #22
Comments
Taking a step back, it's odd that the API defers the closed connection event until the next call to In fact the API should be promoting use of an async context manager: async with trio_websocket.open_connection(host, port) as connection:
try:
...
except ConnectionClosed:
pass |
I think raising at the WebSocket I/O call is the most user-friendly way to do this (see the database example in this post). Cancelling the nursery is pretty unfriendly because the caller has to handle Maybe there needs to be an optional cancel scope for use cases like the example client, where we would prefer for our handler to be cancelled. |
This optional scope is cancelled when the connection is closed.
If the server closes the connection, the client will quit immediately rather than waiting for the next user command on stdin.
I've written up a quick implementation with an optional
I don't love this implementation. It doesn't provide an easy way for the user to know that the server closed the connection. It also adds another argument to the Other possible implementations:
Thoughts? |
Passing in an optional Trio.Event() would be more general than a cancel scope, while avoiding callbacks. I've got a decently sized codebase built from day-one on trio and there's not a single callback to be found. I think the "handle closed connection immediately" use case is real. For example, what if the client only sends a command occasionally, but at those times needs a response as fast as possible. It's important to keep a connection active, reconnecting as needed. However I'm not convinced any new API is necessary, since we already get a signal when the connection closes, by way of the open_websocket_url context manager exiting. Rather it may just be an issue with the structure of this example. I tried to address this simply in #52. But it breaks the regular send and receive case, I can't figure out why. |
I'm not sure how to handle this kind of automagical cancel-on-disconnect situation. (It also comes up on the server side – if a connection drops, should you immediately cancel the connection handler? In an HTTP server, should you pre-emptively cancel a request handler if the client disappears and won't be able to receive the response?) In general in Trio the bias is toward delivering notifications by someone doing In this case, wouldn't it make more sense anyway to have a background task that listens for incoming messages and prints them? and if it gets an EOF, then it could raise |
(ignore my previous comment about a signal already being available-- due to confusion from outdated dependencies) Putting this particular example aside, aggressive reconnect seems like something people will want and, if not supported automatically by the library, there should at least be an example users can look at. Monitoring in a separate task sounds reasonable, but at a minimum |
What's the difference between |
Not much-- I was thinking it might be easier to pass around event or there may be utils which take event. But just using thunks is probably more common. And the event is more dangerous since it's mutable property. |
I'm not 100% sure The thing is, underneath, the only way to actually watch for the socket being closed is to read events off of it. But since you don't want to buffer an arbitrary number of events, if some events arrive and the user hasn't called |
This point about userspace buffering is a very convincing argument in favor of waiting for a
Yes, I see there are two scenarios here. One is the "userspace buffer is full" scenario that Nathaniel discussed. The other is a "no activity on the socket" scenario that you brought up. In your scenario, I think we should recommend users to create a heartbeat task that sends pings. We can provide a recipe for this in the docs. async def heartbeat(ws):
while True:
await trio.sleep(N)
await ws.ping() The I'm not sure if the current Maybe
Yes, this is exactly how it should be. I'll create a new PR with this design. |
One subtlety to watch out for here:
So if we did do this, we'd want to make sure that receiving a pong for a later ping would also wake up earlier pings. (Probably not too hard, but it would require that the ping frames be unique and that we keep a central list of outstanding pings.) |
Oh, wow, good catch in the spec. I think that completely blows up my desire for each call to
Given that users may want to use the ping payload for their own purposes, I lean towards #1. This would require minimal changes to the current implementation and would mostly consist of a recipe in the docs, to go along with the heartbeat recipe mentioned above. |
This is the second attempt at this. Instead of modifying trio-websocket to support cancelling a handler, this commit modifies only the example client itself: it now has one task that gets messages and prints them, and a second task that gets user input and sends messages/commands/etc.
As suggested in this issue thread, there needs to be a way for clients that send infrequent messages to make sure that the connection is still open. Ping/pong are the natural way to do this, but the ping behavior needs to be a bit more robust. This commit adds a `wait_pong()` method that waits for a pong to arrive and returns its payload.
Add a heartbeat recipe to the README. Also add a heartbeat mode to the example client.
I checked how aaugustin's
My inclination would be to copy this design, except make |
Here's the source i was looking at: https://github.com/aaugustin/websockets/blob/master/src/websockets/protocol.py In particular check out the methods |
As suggested in the discussion thread, this commit mimics the ping/pong API in aaugstin's websockets. A call to ping waits for a response with the same payload. (An exception is raised if the payload matches a ping that's already in flight.) A payload is randomly generated if omitted by the caller. Add a new pong() method that can be used for sending an unsolicited pong.
Add a heartbeat recipe to the README. Also add a heartbeat mode to the example client.
I've submitted PR #58 that mimics aaugustin's design. I'll leave 56 and 58 open for now so we can compare the two approaches. |
Example client exits on ConnectionClosed (#22)
The client example is not handling the case where server suddenly drops the connection, since it's blocked waiting for keyboard input.
Also there seems to be no reason that input is being run on a background thread, only to then await on the result.
There should be two tasks running in parallel: one to accept and process commands, and another to loop on
get_message()
and queue the results.The text was updated successfully, but these errors were encountered: