Search
Jednou ze síťových služeb, se kterými lze komunikovat na velmi nízké úrovni (byty) a přesto získat užitečnou informaci, jsou NTP servery. NTP slouží pro synchronizaci hodin počítačů a využívá UDP komunikační protokol na portu 123. Detaily o NTP protokolu a implementaci klienta v C lze najít zde.
Kompletní kód je ke stažení v git repozitáři nebo ve formě balíčku: ppc-sol06.zip
Vytvoření UDP klienta je jednoduché a spočívá ve vytvoření instance třídy QUdpSocket a připojení k hostiteli. Pro práci s adresou hostitele využijeme třídu QHostInfo a připojíme se metodou QAbstractSocket::connectToHost() k některému z časových serverů, které provozuje sdružení CESNET:
QHostInfo host = QHostInfo::fromName("tik.cesnet.cz"); QUdpSocket *s = new QUdpSocket; s->connectToHost(QHostAddress(host.addresses().at(0)), 123);
NTP server očekává požadavek ve formě zprávy o délce právě 48 bytů a právě tak dlouho zprávou klientovi i odpoví. Pro požadavek je klíčový první byte, jehož hodnota je 0x0B1) Existuje pochopitelně několik cest, jak takový paket vytvořit, zde využijeme toho, že na hodnotě ostatních bytů nezáleží a nastavíme všechny na stejnou hodnotu:
0x0B
s->write(QByteArray(48, 0x0B));
Po přijmutí požadavku server odpoví packetem stejné velikosti. Aby klient neukončil svoji činnost dříve, než packet přijde, může využít např. blokující funkci QAbstractSocket::waitForReadyRead(), která čeká na přijetí dat. Argumentem metody je maximální doba čekání (timeout), který je defaultně nastaven na 30000 ms. Po přijetí datagramu lze datagram číst po částech metodou QIODevice::read() nebo celou najednou metodou QIODevice::readAll(). Zda jsou data ke čtení lze zjistit metodou QUdpSocket::hasPendingDatagrams().
if (s->waitForReadyRead()){ while (s->hasPendingDatagrams()) { QByteArray data = s->readAll(); } }
V poli 48 bytů, které vrací server klientovi, je řada užitečných informací. Pro naše účely je zajímavá předposlední čtveřice bytů, číslo typu unsigned int, které reprezentuje počet vteřin od 1.1.1900 00:00:00. Při dekódování zprávy je třeba vyřešit dva problémy:
unsigned int
unsigned long cas = uchar(data[43]) + (uchar(data[42]) << 8) + (uchar(data[41]) << 16) + (uchar(data[40]) << 24); cas -= 2208988800U; QDateTime *time = new QDateTime::fromSecsSinceEpoch(cas); qDebug() << time->toString("yyyy-MM-dd HH:mm:ss");