tommazzo
tommazzo

Reputation: 323

Manage timeouts for multiple UDP sockets

I have to write a TFTP (Trivial File Transfer Protocol) server on Windows and Linux for a university course. I'm using C++ and I want to use one thread and select() to check for new incoming packets. TFTP requires that if a packet is not acknowledged for a certain amount of time, that the packet is re-sent. I'm wondering what the best way is to manage these multiple timeouts.

I was thinking about creating an std::list, which contains objects that associate a connection with the absolute time at which the timeout occurs. The list is ordered by increasing timeout times (all timeouts are the same when they are assigned, so a new timeout is always the greatest and can go to the end of the list - otherwise I would need a map instead of the list).
Since I need to reset the timeout for a connection, if a packet arrives in time, I want to create an std::map that associates a connection with an iterator pointing to its place in the list. When a connection's timeout is updated the element in the list can be found quickly, updated and moved to the end of the list (again assuming that a new timeout is the greatest).

Is this a good way to handle the problem or is there anything simpler?

Upvotes: 0

Views: 784

Answers (1)

Gabriel
Gabriel

Reputation: 3097

If I understand it correctly, you have pairs of connection/timeout, and you want to be able to access those pairs by connection as well as by timeout. By connection because you have to change the timeout when you receive a packet, and by timeout because you need to know what is the next connection to timeout.

If you have nothing against boost, take a look at multi_index.

If you want to roll your own, you may keep two sets of pointers, giving to the set different comparison functions:

class Connection {
    ...
public:
    int GetTimeout() const;
    int GetID() const;
};

class TimeIsLess {
public:
    bool operator()(const Connection*c1, const Connection*c2) const {
        return c1->GetTimeout() < c2->GetTimeout();
    }
}
class IdIsLess {
public:
    bool operator()(const Connection*c1, const Connection*c2) const {
        return c1->GetId() < c2->GetId();
    }
}

std::set<Connection*,TimeIsLess> connectionsByTime;
std::set<Connection*,IdIsLess> connectionsById;

To create a connection:

...
Connection * c = new Connection(id, timeout);
connectionsByTime.insert(c);
connectionsById.insert(c);
...

To get the next connection that will timeout, just get the first one:

auto nextToTimeout = connectionsByTime.begin();
if (nextToTimeout != connectionsByTime.end())
{
    if ( (*nextToTimeout)->GetTimeout() < now )
    {
        // Close the connection
    }
}

To remove a connection, you'de have to remove the pointer from one set, and remove AND delete the pointer from the other set.

I compiled none of it so don't nail me on the typos (:

Upvotes: 1

Related Questions