edgarstack
edgarstack

Reputation: 1116

Python: Setting multiple continuous timeouts

I want to have some kind of server that receives events (i.e using sockets), and each event has a different ID (i.e dst port number).

Is there a way that from the moment I see the first packet of an specific ID, I start some kind of timeout (i.e, 1ms), and if in that time nothing else with the same ID is received an event is triggered, but if something is received the timeout is reset to 1ms.

I have seen that something like that can be done by using signals and the SIGALARM signal. However, I want to keep multiple "timers" for every different ID.

Upvotes: 1

Views: 774

Answers (2)

Gil Hamilton
Gil Hamilton

Reputation: 12347

Sounds like a job for select. As you are using sockets, you have a socket descriptor for a client (presumably one for each client but as long as you have one, it works). So you either want to wait until a packet arrives on one of your sockets or until a timeout occurs. This is exactly what select does.

So calculate the expiration time for each client when you receive a message, then in your main loop, simply calculate the soonest-to-expire timeout and provide that as the timeout parameter to select.select (with all the socket descriptors as the rlist parameter). Then you get awakened when a new packet/message arrives or when the oldest timeout expires. If it's a new packet, you process the packet and reset that provider's timeout to 1ms; otherwise, you do whatever you do when the timeout expires.

Then calculate the next-to-expire timeout. Rinse. Lather. Repeat.

Something like this:

now = time.time()
timeout = min([(client.expiration - now) for client in clients_list])
rrdy, wrdy, xrdy = select.select([client.sock for client in clients_list], [], [], timeout)
if not rrdy:
    # Timeout
    now = time.time()
    for client in clients_list:
        if client.expiration < now:
            process_timeout(client)
else:
    # Process incoming messages
    for rsock in rrdy:
        process_message(rsock.recv(4096))
        client.expiration = time.time() + .001

Upvotes: 2

See the sched built-in module, which has a scheduler.

You can construct a new scheduler instance, then use scheduler.enter to schedule a function to be called after a delay; and if you receive a message within the time limit, you can remove its event from the queue using scheduler.cancel(event); you can use the scheduler.run() to run the scheduler in another thread, or you can use scheduler.run(blocking=False) in a select-multiplexing thread with timeouts.

Upvotes: 1

Related Questions