0

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?

tommazzo
  • 323
  • 1
  • 4
  • 13
  • 1
    Very similar: http://stackoverflow.com/questions/7075472/scalable-algorithm-to-detect-stale-data/7084735#7084735 – Lior Kogan Sep 23 '11 at 19:29
  • Beware of keeping iterators pointing to a list you are going do modify. In this case, moving an item to the end of the list will invalidate the iterators held by the map. – Gabriel Sep 23 '11 at 21:07
  • Thank you guys for your help. Actually only the iterator to the moved item would be invalid. The others would still remain valid. See [http://www.sgi.com/tech/stl/List.html](http://www.sgi.com/tech/stl/List.html). But I'm going to use the version, where I keep the items in a set, like you suggested, rather than a list (see my comment below). – tommazzo Sep 24 '11 at 20:37

1 Answers1

1

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 (:

Gabriel
  • 2,841
  • 4
  • 33
  • 43
  • Thank you. That's what I'm going to do. Actually have to do a modified version of it, which uses one map and a set. I want to write a generic class that can be used for other timeouts as well. So I'm going to have a class called TimeoutKeeper that associates an object with its timeout. So my two collections are going to be a map *> and like you suggested a set *>. Thank you once again for your help. – tommazzo Sep 24 '11 at 20:35
  • 1
    @tommazzo Glad to help (: Hey, maybe you want to encapsulate the housekeeping in a single class, which would contain (and hide away) the map and the set. It can have an AddConnection(), a GetConnectionByTime(), a GetConnectionByID() and a RemoveConnection(). I also like to make the difference between Get...() which searches the internal structures for the given object and, adds it if not found, and Find...() which only searches for the given object and returns NULL or an error code when not found. Cheers! – Gabriel Sep 25 '11 at 12:05
  • Of course I'm encapsulating all of this in a separate class. In fact I want to re-use it in another scenario within the same program, where I have to manage the timeouts of file mappings in a cache (the methods are going to have a generic name, like e.g. addElement()). Thanks, once again for your help. – tommazzo Sep 26 '11 at 09:16