Reputation: 323
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
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