Boost Asio: Atomuhr per UDP abfragen



  • Hallo,
    ich probiere zurzeit, die Atomuhr mit UDP von dem Server europe.pool.ntp.org:123 abzufragen.
    Das Programm schreibt den Input des Benutzers in eine Datei. Immer, wenn er [Enter] drückt, soll das Programm in die nächste Zeile der Datei die Uhrzeit und das Datum schreiben.
    Leider funktioniert die Zeitabfrage nicht. Das Programm blockt bei der Funktion

    socket.receive_from(boost::asio::buffer(buf, bufSize), here);
    

    Wenn ich es debugge, steht unter anderem

    #1  0x0000000000409e74 in boost::asio::detail::socket_ops::recvfrom (s=7, bufs=0x7fffffffdbc0, count=1, flags=0, addr=0x7fffffffdcc0, addrlen=0x7fffffffdbb8, ec=...) at ../../../../../../../../home/florian/boost/boost_1_60_0/boost_1_60_0/boost/asio/detail/impl/socket_ops.ipp:934
    

    In socket_ops.ipp:934 steht

    signed_size_type result = error_wrapper(::recvmsg(s, &msg, flags), ec);
    

    Ich bin mir sicher, dass der Fehler in meiner Funktion Time::update liegt, wo die aktuelle Zeit abgefragt und in einem tm-Pointer verarbeitet wird.
    Time::Time und Time::update

    Time::Time(boost::asio::io_service& ioS)
    	: socket(ioS), resolver(ioS), query(boost::asio::ip::udp::v4(), "europe.pool.ntp.org", "123"), timeServer(*resolver.resolve(query)), pTime(std::make_unique<tm>()){
    
    	buf[12] = {0};
    	bufSize = sizeof(buf);
    
    	static_assert(sizeof(unsigned int) == 4 || sizeof(unsigned int) == 8, "size_t has to be four or eight bytes long.");
    	buf[0] = htonl((3 << 27) | (3 << 24)); 		//siehe RFC 5905 [NTP]
    }
    
    void Time::update(){
        boost::asio::ip::udp::endpoint here;
    	socket.open(boost::asio::ip::udp::v4());
    	socket.send_to(boost::asio::buffer(buf, bufSize), timeServer);
        std::cout << "socket::receive_from aufgerufen." << std::endl;
    	socket.receive_from(boost::asio::buffer(buf, bufSize), here);
    	std::cout << "ntohl aufgerufen." << std::endl;
    	time_t secs = ntohl(buf[8]) - 2208988800u;
    	pTime = std::make_unique<tm>(*localtime(&secs));
    	secfrac =  static_cast<double>(ntohl(buf[9])/4294967296.0);
    

    LG 🙂



  • Ich würde mal darauf tippen dass dein socket.receive_from blockiert weil keine Antwort vom Server empfangen wird.
    Was jetzt an mehreren Dingen liegen kann.
    Könnte sein der Server antwortet nicht, weil er deine Anfrage nicht bekommen hat.
    Könnte sein der Server antwortet nicht, weil deine Anfrage fehlerhaft war.
    Könnte sein der Server antwortet, das Paket kommt aber nicht zu deinem PC durch weil dein Router es filtert.
    Uswusf.

    Dass der Server nicht antwortet kann man allerdings im Realbetrieb sowieso nicht ausschliessen -- auch nicht wenn dein Programm und Netzwerksetup fehlerfrei ist. Der Fall gehört also irgendwie behandelt. D.h. ich würde als erstes mal ein Timeout beim Empfangen einbauen.



  • Dass der Server (in meinem Falle) nicht antwortet, würde ich ausschließen, da ich das Programm auch mit einem anderem Zeitserver getestet habe: (ptbtime2.ptb.de:123). Dass die Anfrage fehlerhaft war, könnte sein, aber eigentlich sollte sie richtig sein. (Ich habe mich hier an meinem C++-Buch orientiert.)
    Ergibt es eigentlich Sinn, die Zeit vom Server abzufragen, anstatt von der Systemzeit? Ich meine, bis der Server antwortet, kann der Benutzer schon viel mehr geschrieben haben. Dadurch wird ja der Input, der geschrieben wird, wenn die Serverabfrage blockiert, nicht geloggt.
    Allerdings kann man die Systemzeit verändern, um den Log zu verfälschen, wenn man die Zeit vom System abfragt.
    Sollte ich trotzdem lieber die Zeit von der Systemzeit abfragen?

    Edit: Ok, mein Problem mit der Zeitabfrage hat sich erledigt. Ich hatte einen Fehler in Zeile 9. So sieht sie richtig aus.

    static_assert(sizeof(unsigned int) == 4, "size_t has to be four bytes long.");
    


  • isko schrieb:

    Ergibt es eigentlich Sinn, die Zeit vom Server abzufragen, anstatt von der Systemzeit? Ich meine, bis der Server antwortet, kann der Benutzer schon viel mehr geschrieben haben. Dadurch wird ja der Input, der geschrieben wird, wenn die Serverabfrage blockiert, nicht geloggt.

    Ich würde da auf jeden Fall die Systemzeit verwenden. Wenn du alle paar Sekunden ne Anfrage an nen Time-Server schickst, dann wird dich der Time-Server irgendwann nicht mehr lieb haben. (OK, so Time-Server sind üblicherweise mit dicken Pipes angebunden -- trotzdem ist es schlechter Stil die völlig ohne Not zu hämmern.)

    isko schrieb:

    Allerdings kann man die Systemzeit verändern, um den Log zu verfälschen, wenn man die Zeit vom System abfragt.
    Sollte ich trotzdem lieber die Zeit von der Systemzeit abfragen?

    Jedes mir bekannte OS bietet eine "monotonic clock" die von der Systemzeit unabhängig ist.
    Wenn du unbedingt die Zeit gegen einen Time-Server checken willst, dann kannst du ja bei Programmstart 1x den Time-Server fragen. In dem Moment wo du die Antwort bekommst fragst du zusätzlich die Systemzeit und die "monotonic clock" ab.
    Dann kannst du zu jedem Zeitpunkt prüfen ob jemand an der Zeit geschraubt hat.

    Bzw. es gibt natürlich unendlich viele Varianten wie man es machen kann. Kommt wohl ganz darauf an wie wichtig es ist dass die geloggte Zeit "stimmt".
    Gibt z.B. auch noch die Möglichkeit ein Timestamping Service zu verwenden. Damit bekommst du nicht nur ne zuverlässige Zeit, sondern noch dazu ne Signatur mit einem anerkannten (mehr oder weniger - je nachdem welches Timestamping Service du verwendest) Zertifikat. Was du dabei signieren lässt und wie oft kannst du dir aussuchen. z.B. könntest du alle paar Minuten den gesamten Inhalt des Logfiles signieren lassen. Oder alles was seit der letzten Signatur dazugekommen ist. Oder ...

    isko schrieb:

    Edit: Ok, mein Problem mit der Zeitabfrage hat sich erledigt. Ich hatte einen Fehler in Zeile 9. So sieht sie richtig aus.

    static_assert(sizeof(unsigned int) == 4, "size_t has to be four bytes long.");
    

    Naja ich würde sagen richtig wäre

    static_assert(sizeof(unsigned int) == 4, "unsigned int has to be four bytes long.");
    

    Und noch besser wäre vermutlich

    static_assert(sizeof(uint32_t) == 4, "uint32_t has to be four bytes long.");
    

    Und noch noch besser wäre die Message in einem unsigned char Array aufzubauen - dann ist wurst wie lange irgendwelche Datentypen sind.

    😉



  • Okay, vielen Dank für deine Hilfe.

    hustbaer schrieb:

    isko schrieb:

    Edit: Ok, mein Problem mit der Zeitabfrage hat sich erledigt. Ich hatte einen Fehler in Zeile 9. So sieht sie richtig aus.

    static_assert(sizeof(unsigned int) == 4, "size_t has to be four bytes long.");
    

    Naja ich würde sagen richtig wäre

    static_assert(sizeof(unsigned int) == 4, "unsigned int has to be four bytes long.");
    

    Ja stimmt, ich hatte zuerst ein size_t-Array benutzt. Allerdings ging das nicht, da NTP 12 32-Bit-Felder benötigt und nicht 12 64-Bit-Felder.

    hustbaer schrieb:

    Und noch noch besser wäre die Message in einem unsigned char Array aufzubauen - dann ist wurst wie lange irgendwelche Datentypen sind.

    Warum? Wenn ich ein usigned char Array benutzen würde, hätte ich doch 12 8-Bit-Felder und das würde doch nicht klappen.



  • Na dann nimmste vielleicht besser 48 chars? 😕


Anmelden zum Antworten