Alternativer Timer zu Sleep



  • Du verwendest einfach einen einzigen UDP Socket, und zwar non-blocking.

    Jede Connection ist dabei ein Objekt einer selbst geschriebenen Klasse.
    Jede Connection weiss zu jedem Zeitpunkt:
    * ob sie ein Packerl schicken möchte
    * ob sie ein Packerl erwartet
    * bis zu welchem Zeitpunkt max. gewartet werden sollte
    * was beim Timeout passieren soll (z.B. gesendetes Paket neu übertragen oder aufgeben)
    * ...

    In deiner Hauptschleife ermittelst du dann ob irgendeine Connection ein Paket senden möchte. Wenn ja, rufst du select mit "read, write" auf, wenn nein rufst du select bloss mit "read" auf. Timeout für select nimmst du irgendwas kleines, vielleicht 10ms.

    Wenn select zurückkommt machst du folgendes:
    * so oft recvfrom aufrufen bis nixmehr ankommt (vielleicht mit einem Limit von max. 1000 Paketen oder so, falls die CPU langsamer sein sollte als das Netz, was unwahrscheinlich ist, aber theoretisch denkbar)
    * alle Connections checken, ob sie ein Paket senden möchten, und das Paket senden, so lange bis entweder keine Connection mehr senden will, oder sendto EWOULDBLOCK zurückgibt.

    -> zurück zu select()

    void foo()
    {
        forever
        {
            select_mode = read;
            if (any_connection_wants_to_send)
               select_mode = read | write;
    
            select(socket, select_mode, timeout = 10ms);
    
            while (recvfrom(socket, buffer, &sender_address) == OK)
            {
                connection = find_connection(sender_address);
                connection->process_packet(buffer);
            }
    
            for all connections
            {
                connection->tick(); // hier behandelt man z.b. timeouts etc.
    
                if (connection->wants_to_send())
                {
                    buffer = connection->get_send_packet();
                    dest_address = connection->get_peer_address();
                    if (sendto(socket, dest_address) == OK)
                        connection->packet_sent_successfully();
                    else
                        break; // vermutlich EWOULDBLOCK weil sende-puffer voll
                }
            }
        }
    }
    

    Das ist jetzt mal grob, und total unoptimiert.

    Natürlich muss man nicht jede Connection immer "ticken", und nicht jede Connection checken ob sie ein Paket senden möchte etc. Das lässt sich einfach mit diversen Queues lösen, so dass man nur immer die Connections checken muss, die auch was tun wollen.


Anmelden zum Antworten