PDA

View Full Version : Ntp client does not work! urgent!



kknd
31st March 2010, 11:40
Hi, I am trying to write a ntp client which is going to connect to a time server and get the time..could'nt find qt example although it is possiple to find many c, vc++ or c# clients.. my code does connect but does not get any datagrams, have you any idea? here is my code, i need first to retrieve the time, i did not code rest of it yet.


void AtomicClock::on_TimeUpdate_clicked()
{
QHostInfo info = QHostInfo::fromName("0.pool.ntp.org");
QString ipAddress = info.addresses().first().toString();
ui->ipAddress->setText(ipAddress);
udpSocket = new QUdpSocket(this);
udpSocket->connectToHost("0.pool.ntp.org", 123);

connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(udpSocket, SIGNAL(connected()), this, SLOT(connectSuccsess()));
}
void AtomicClock::readPendingDatagrams()
{
ui->statusLabel->setText("Reading...");

while(udpSocket->hasPendingDatagrams())
{
QByteArray buffer(udpSocket->pendingDatagramSize(), 0);
udpSocket->readDatagram(buffer.data(), buffer.size());
QDataStream stream(buffer);
stream.setVersion(QDataStream::Qt_4_6);
QString newTime;
stream >> newTime;
ui->lineEdit->setText(newTime);
}
}
void AtomicClock::connectSuccsess()
{
ui->time->setText("Connected");
QByteArray timeRequest(48,'0');
QDataStream out(&timeRequest, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_6);

udpSocket->writeDatagram(timeRequest, QHostAddress("0.pool.ntp.org"), 123);
}

boudie
31st March 2010, 20:26
Change the order of these instructions:


udpSocket->connectToHost("0.pool.ntp.org", 123);

connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(udpSocket, SIGNAL(connected()), this, SLOT(connectSuccsess()));


into this:


connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(udpSocket, SIGNAL(connected()), this, SLOT(connectSuccsess()));

udpSocket->connectToHost("0.pool.ntp.org", 123);

kknd
31st March 2010, 21:12
tried but still does not work out, connect works with connectToHost but readReady never emits, with use of bind* none of them works neither connect nor readReady..still dont know what is wrong, i need timestamp to build my ntp client further.
void AtomicClock::on_TimeUpdate_clicked()
{
QHostInfo info = QHostInfo::fromName("0.pool.ntp.org");
QString ipAddress = info.addresses().first().toString();
ui->ipAddress->setText(ipAddress);
udpSocket = new QUdpSocket(this);

connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(udpSocket, SIGNAL(connected()), this, SLOT(connectSuccsess()));

udpSocket->connectToHost("0.pool.ntp.org", 123);
//udpSocket->bind(QHostAddress(ipAddress),123); *tried that too, to connect to server with bind, because i use writedatagram and readdatagram
}

squidge
31st March 2010, 23:18
Firstly, marking your thread as "Urgent" will not get people to reply any quicker to your topic, and in some cases, some people may not reply at all. Remember, we are doing this out of our own free time. We are not being paid.

Secondly, UDP is a connectionless and lossy protocol. There's no guarantee any of the packets you send out will arrive at there destination, less so if you are going through several routers to the host.

Some servers may also require authorisation - have you checked the server is accessible with other programs?

wysota
31st March 2010, 23:28
Oh... an "urgent" thread, lol :D

I have one question for you - why are you using QDataStream? Not that you actually send some data to it but you do realize no NTP server will know how to deserialize data serialized with QDataStream, right?

ChrisW67
1st April 2010, 01:15
Just a quick test using netcat against my NTP server shows that the intent of AtomicClock::connectSuccsess(), which is sending a stream of 48 '0' characters to the server (did you mean NUL characters?), does not elicit a response. Perhaps you need to send a well-formed NTP request to the server?

I would suggest getting a hold of the NTP server code from ntp.org and looking at the source to the bundled ntpdate program.

kknd
1st April 2010, 11:33
Hello,

to fatjuicymole, i know that i have just written that after i couldnt make work with this small code about 3 days, its just feeiling and wish..
second, i have checked the server with windows ntp client and few other free programs too, it works and the server address i use is a free organization server ntp.org. actually i used my code for other servers too so same problem occurs again.

to wysota, I dindnt think in that way, i try to write just a client program, i need to use QByteArray for time request. It is the same way the other codes do same jobb.. no way tried without datastream, and tried with connectToHost, write, read does not call readPendingDatagrams() anyway.

to ChrisW67, yeah i ment to send null characters with 48 length.

^NyAw^
1st April 2010, 12:17
Hi,





udpSocket->writeDatagram(timeRequest, QHostAddress("0.pool.ntp.org"), 123);


You can try to force a flush after sending datagram.


udpSocket->flush();
Also use Wireshark sniffer to look if your data is sent and you recieve data from the server.

faldzip
1st April 2010, 12:26
Hello,
to ChrisW67, yeah i ment to send null characters with 48 length.

So 4 zeros or 4 null characters?


QChar zeroChar = '0';
QChar nullChar = '\0';

kknd
1st April 2010, 13:28
use of flush() makes no difference, i mean char '0', saw that in a c# example..

wysota
1st April 2010, 17:14
use of flush() makes no difference, i mean char '0', saw that in a c# example..

It would be nice if you actually checked what you were supposed to send instead of randomly trying your luck. Not very efficient...

ChrisW67
2nd April 2010, 03:36
RFC 1305 Section 3 and Appendix A. The packet format is substantially more complex than an 48-byte block of zero bytes. This is a request and response on my machine.


Frame 1 (90 bytes on wire, 90 bytes captured)
Ethernet II, Src: 192.168.1.6 (00:1a:4d:70:61:9c), Dst: CameoCom_c1:3e:76 (00:40:f4:c1:3e:76)
Internet Protocol, Src: 192.168.1.6 (192.168.1.6), Dst: 192.168.1.1 (192.168.1.1)
User Datagram Protocol, Src Port: 53531 (53531), Dst Port: ntp (123)
Network Time Protocol
Flags: 0xe3
11.. .... = Leap Indicator: alarm condition (clock not synchronized) (3)
..10 0... = Version number: NTP Version 4 (4)
.... .011 = Mode: client (3)
Peer Clock Stratum: unspecified or unavailable (0)
Peer Polling Interval: 4 (16 sec)
Peer Clock Precision: 0.015625 sec
Root Delay: 1.0000 sec
Root Dispersion: 1.0000 sec
Reference Clock ID: NULL
Reference Clock Update Time: NULL
Originate Time Stamp: NULL
Receive Time Stamp: NULL
Transmit Time Stamp: Apr 2, 2010 02:12:51.5572 UTC

0000 00 40 f4 c1 3e 76 00 1a 4d 70 61 9c 08 00 45 00 .@..>v..Mpa...E.
0010 00 4c 00 00 40 00 40 11 b7 49 c0 a8 01 06 c0 a8 .L..@.@..I......
0020 01 01 d1 1b 00 7b 00 38 83 a1 e3 00 04 fa 00 01 .....{.8........
0030 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050 00 00 cf 5f d1 23 8e a1 83 72 ..._.#...r

No. Time Source Destination Protocol Info
2 0.000254 192.168.1.1 192.168.1.6 NTP NTP server

Frame 2 (90 bytes on wire, 90 bytes captured)
Ethernet II, Src: CameoCom_c1:3e:76 (00:40:f4:c1:3e:76), Dst: 192.168.1.6 (00:1a:4d:70:61:9c)
Internet Protocol, Src: 192.168.1.1 (192.168.1.1), Dst: 192.168.1.6 (192.168.1.6)
User Datagram Protocol, Src Port: ntp (123), Dst Port: 53531 (53531)
Network Time Protocol
Flags: 0x24
00.. .... = Leap Indicator: no warning (0)
..10 0... = Version number: NTP Version 4 (4)
.... .100 = Mode: server (4)
Peer Clock Stratum: secondary reference (3)
Peer Polling Interval: 4 (16 sec)
Peer Clock Precision: 0.000001 sec
Root Delay: 0.0388 sec
Root Dispersion: 0.1307 sec
Reference Clock ID: 192.231.203.132
Reference Clock Update Time: Apr 2, 2010 02:05:40.4534 UTC
Originate Time Stamp: Apr 2, 2010 02:12:51.5572 UTC
Receive Time Stamp: Apr 2, 2010 02:12:51.5165 UTC
Transmit Time Stamp: Apr 2, 2010 02:12:51.5165 UTC

0000 00 1a 4d 70 61 9c 00 40 f4 c1 3e 76 08 00 45 00 ..Mpa..@..>v..E.
0010 00 4c 00 00 40 00 40 11 b7 49 c0 a8 01 01 c0 a8 .L..@.@..I......
0020 01 06 00 7b d1 1b 00 38 82 6e 24 03 04 ec 00 00 ...{...8.n$.....
0030 09 ef 00 00 21 73 c0 e7 cb 84 cf 5f cf 74 74 10 ....!s....._.tt.
0040 e8 71 cf 5f d1 23 8e a1 83 72 cf 5f d1 23 84 38 .q._.#...r._.#.8
0050 8d a6 cf 5f d1 23 84 3b c2 52 ..._.#.;.R


In the request the first UDP payload byte is the "e3" on line 23 of the listing.

kknd
2nd April 2010, 11:31
thanks to everyone, now it is one step ahead. the code works as a client and retrieves server response. i used wireshark(it didnt like wireless but got the frames with cable connection) to examine the client and server packets, i used my code and an SNTPClient Solution from DaveyM69, the problem was the udp request(as many of you mentioned), i used very stupid(because its not complex) way to code my request. its shown below, now the problem is to get the data(i couldnt read it or show it, dont know why)..


void AtomicClock::connectSuccsess()
{
ui->time->setText("Connected");
QByteArray timeRequest(48, 0);
timeRequest[0] = 0x23; //Obs! first byte, important, makes a ntp client version 4, lazy way
//the rest of the btes sends null, it means very wrong timestamps, but i dont need them now..just get the
//time from server.
udpSocket->write(timeRequest);
}

the second part which is stll not working properly..

void AtomicClock::readPendingDatagrams()
{
ui->statusLabel->setText("Reading...");
QByteArray newTime;

do {
newTime.resize(udpSocket->pendingDatagramSize());
udpSocket->read(newTime.data(), newTime.size());
} while(udpSocket->hasPendingDatagrams());

QString dateTime;
QDataStream in(&newTime, QIODevice::ReadOnly);
in.setVersion(QDataStream::Qt_4_6);
in >> dateTime;
ui->textEdit->setText(dateTime);
}

ChrisW67
3rd April 2010, 01:31
You are not getting a response because there's insufficient information in the request you are sending. The minimum that seems to elicit a response from my NTP server is (hex):


echo -ne "\xe3\x00\x04\xfa\x00\x01\x00\x00\x00\x01\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | nc -u ptolemy 123 | od -tx1
0000000 24 03 04 ec 00 00 09 ac 00 00 28 4a c0 e7 cb 84
0000020 cf 61 03 93 73 5d d6 6e 00 00 00 00 00 00 00 00
0000040 cf 61 09 af 61 26 62 2c cf 61 09 af 61 29 fa 5e

The first ten bytes have values. (nc is the netcat utility)

You still need to disassemble the 64-bit time stamp into something that you can use as a time.

kknd
3rd April 2010, 12:48
Hi, I managed to get response from the ntp server, if you just see my code up there. now the problem is to get the transmit timestamp or read it in a proper way to get the time UTC 1.1.1970 + seconds.. I changed my readPendingDatagrams() function..but it gives ugly reply if i run the code. Can i get little bit help for that? i am just conderned wtih last 8 bytes(transmit stamp)..thanks.

void AtomicClock::readPendingDatagrams()
{
ui->statusLabel->setText("Reading...");
QByteArray newTime;
QDateTime Epoch(QDate(1900, 1, 1));
do {
newTime.resize(udpSocket->pendingDatagramSize());
udpSocket->read(newTime.data(), newTime.size());
} while(udpSocket->hasPendingDatagrams());

QByteArray TransmitTimeStamp ;
TransmitTimeStamp = newTime.right(8);

quint64 seconds = 0;
for (int i=0; i<= 3; i++)
{
seconds = (seconds << 8) | TransmitTimeStamp[i];
}
quint64 fractions = 0;
for (int i=4; i<= 7; i++)
{
fractions = (fractions << 8) | TransmitTimeStamp[i];
}
double ticks = seconds + (fractions / 0x100000000L);

ui->textEdit->append(QString::number(ticks,' ', 0));
//QDateTime newestTime = Epoch + ticks;

kknd
3rd April 2010, 23:10
yes, got it! even i cant call that code perfect, it works, sends ntp client request ver. 4 to server on udp port 123 and gets server response 48 bytes, after that TransmitTimeStamp is choosen from the bytearray last (8 bytes), actually i used just the seconds the first 4 bytes but not last 4 bytes(fraction) dont need to be very sensitive within this prog. code includes some labels and texedit objects, the purpose is just to see on gui how the code would run.. i copy my code here, so maybe someone needs in the future. thanks eveyone on this thread..got much help with wireshark, and ntp request advices..


#ifndef ATOMICCLOCK_H
#define ATOMICCLOCK_H

#include <QMainWindow>
#include <QAbstractSocket>
#include <QUdpSocket>
#include <QHostInfo>
#include <QHostAddress>
#include <QDateTime>
namespace Ui {
class AtomicClock;
}

class AtomicClock : public QMainWindow {
Q_OBJECT
public:
AtomicClock(QWidget *parent = 0);
~AtomicClock();

protected:
void changeEvent(QEvent *e);

private:
Ui::AtomicClock *ui;
QUdpSocket *udpSocket;

private slots:
void on_TimeUpdate_clicked();
void readPendingDatagrams();
void connectSuccsess();
};

#endif // ATOMICCLOCK_H


#include "atomicclock.h"
#include "ui_atomicclock.h"

AtomicClock::AtomicClock(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::AtomicClock)
{
ui->setupUi(this);
}
AtomicClock::~AtomicClock()
{
delete ui;
udpSocket->close();
}
void AtomicClock::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void AtomicClock::on_TimeUpdate_clicked()
{
QHostInfo info = QHostInfo::fromName("0.pool.ntp.org");
QString ipAddress = info.addresses().first().toString();
ui->ipAddress->setText(ipAddress);
udpSocket = new QUdpSocket(this);

connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(udpSocket, SIGNAL(connected()), this, SLOT(connectSuccsess()));
udpSocket->abort();
udpSocket->connectToHost("0.pool.ntp.org", 123);
}
void AtomicClock::readPendingDatagrams()
{
ui->statusLabel->setText("Reading...");
QByteArray newTime;
QDateTime Epoch(QDate(1900, 1, 1));
QDateTime unixStart(QDate(1970, 1, 1));
do {
newTime.resize(udpSocket->pendingDatagramSize());
udpSocket->read(newTime.data(), newTime.size());
} while(udpSocket->hasPendingDatagrams());

QByteArray TransmitTimeStamp ;
TransmitTimeStamp = newTime.right(8);

quint64 seconds = 0;
for (int i=0; i<= 3; ++i)
{
seconds = (seconds << 8) | TransmitTimeStamp[i];
}

ui->textEdit->append(QString::number(seconds, 10));
QDateTime newestTime;
newestTime.setTime_t(seconds - Epoch.secsTo(unixStart));
ui->textEdit->append(newestTime.toString());
}
void AtomicClock::connectSuccsess()
{
ui->time->setText("Connected");
QByteArray timeRequest(48, 0);
timeRequest[0] = '\x23';
udpSocket->flush();
udpSocket->write(timeRequest);
}

kknd
9th April 2010, 18:03
hi again,

the code over there sometimes does not work, do you know why? i need to make it correct, nedd help of you , guys!

can not get (or calculate wrong) correct time from server..

thanks in advance


#
void AtomicClock::readPendingDatagrams()
#
{
#
ui->statusLabel->setText("Reading...");
#
QByteArray newTime;
#
QDateTime Epoch(QDate(1900, 1, 1));
#
QDateTime unixStart(QDate(1970, 1, 1));
#
do {
#
newTime.resize(udpSocket->pendingDatagramSize());
#
udpSocket->read(newTime.data(), newTime.size());
#
} while(udpSocket->hasPendingDatagrams());
#

#
QByteArray TransmitTimeStamp ;
#
TransmitTimeStamp = newTime.right(8);
#

#
quint64 seconds = 0;
#
for (int i=0; i<= 3; ++i)
#
{
#
seconds = (seconds << 8) | TransmitTimeStamp[i];
#
}
#

#
ui->textEdit->append(QString::number(seconds, 10));
#
QDateTime newestTime;
#
newestTime.setTime_t(seconds - Epoch.secsTo(unixStart));
#
ui->textEdit->append(newestTime.toString());

squidge
9th April 2010, 19:21
So what happens instead of what you are expecting?

kknd
9th April 2010, 20:20
i am expecting to get transmitt timestamp from server response TransmitTimeStamp = newTime.right(8); last 8 bytes, i use the first 4 bytes to get the date/time, not bothering last 4 bytes (fraction part). og i get often
seconds 18446744073709551601
(quint64) Epoch.secsTo(unixStart) 18446744071623573120
and newestTime.toString() th 7. feb 07:28:01 2036 year 2036, very wrong date

i just copy the code again, made little bit justify after last try... any idea?

void AtomicClock::readPendingDatagrams()
{
QByteArray newTime;
QDateTime Epoch(QDate(1900, 1, 1));
QDateTime unixStart(QDate(1970, 1, 1));
do {
newTime.resize(udpSocket->pendingDatagramSize());
udpSocket->read(newTime.data(), newTime.size());
} while(udpSocket->hasPendingDatagrams());

QByteArray TransmitTimeStamp ;
TransmitTimeStamp = newTime.right(8);

quint64 seconds = 0;
for (int i=0; i<=3; ++i)
{
seconds = (seconds << 8) | TransmitTimeStamp[i];
}

ui->textEdit->append(QString::number(seconds, 10));
QDateTime newestTime;
quint64 diffe = seconds - (quint64) Epoch.secsTo(unixStart);
newestTime.setTime_t(diffe);
ui->textEdit->append(QString::number((quint64) Epoch.secsTo(unixStart), 10));
ui->textEdit->append(newestTime.toString());
}

squidge
9th April 2010, 21:06
Since year 2036 is when the date used by SNTP overflows, it would seem that you are receiving an invalid time from the server (possibly 0xFFFFFFFFFFFFFFFF) or your buffering code is incorrect.

What data are you receiving from the server?

kknd
9th April 2010, 22:15
for example i got print
1
18446744072894420334
18446744071623573120
fr 9. apr 23:06:54 2010
3
18446744073709551521
18446744071623573120
to 7. feb 07:26:41 2036

one correct, one wrong, just in 2 seconds after i clicked connect time server button. here is the wrong time i got(or calculated after i got the udp packet)

No. Time Source Destination Protocol Info
1725 860.103066 207.46.232.182 192.168.1.104 NTP NTP server

Frame 1725 (90 bytes on wire, 90 bytes captured)
Ethernet II, Src: Arcadyan_98:a7:bc (00:1a:2a:98:a7:bc), Dst: CompalCo_66:ef:cb (00:16:d4:66:ef:cb)
Internet Protocol, Src: 207.46.232.182 (207.46.232.182), Dst: 192.168.1.104 (192.168.1.104)
User Datagram Protocol, Src Port: ntp (123), Dst Port: 19277 (19277)
Network Time Protocol
Flags: 0x1c
00.. .... = Leap Indicator: no warning (0)
..01 1... = Version number: NTP Version 3 (3)
.... .100 = Mode: server (4)
Peer Clock Stratum: secondary reference (3)
Peer Polling Interval: invalid (0)
Peer Clock Precision: 0,015625 sec
Root Delay: 0,0716 sec
Root Dispersion: 0,0804 sec
Reference Clock ID: 69.36.241.112
Reference Clock Update Time: Apr 9, 2010 21:07:27,5338 UTC
Originate Time Stamp: NULL
Receive Time Stamp: Apr 9, 2010 21:11:45,3768 UTC
Transmit Time Stamp: Apr 9, 2010 21:11:45,3768 UTC

0000 00 16 d4 66 ef cb 00 1a 2a 98 a7 bc 08 00 45 00 ...f....*.....E.
0010 00 4c c3 71 00 00 76 11 07 3a cf 2e e8 b6 c0 a8 .L.q..v..:......
0020 01 68 00 7b 4b 4d 00 38 13 4f 1c 03 00 fa 00 00 .h.{KM.8.O......
0030 12 56 00 00 14 92 45 24 f1 70 cf 6a 15 8f 88 a4 .V....E$.p.j....
0040 aa 6e 00 00 00 00 00 00 00 00 cf 6a 16 91 60 73 .n.........j..`s
0050 83 85 cf 6a 16 91 60 73 83 85 ...j..`s..

squidge
9th April 2010, 23:01
Yes, that's because your readPendingDatagrams method is incorrect. You should read into a buffer and then take the last 8 bytes of that buffer. Currently you are just overwriting the same buffer until you run out of bytes to read, so the buffer can be invalid unless it is segmented right by the tcp protocol. So it will work sometimes and not at all at others.

kknd
10th April 2010, 17:13
I see, but how to code that, any idea? could not code more then that.. i can not still get the correct time but sometimes..


while (udpSocket->hasPendingDatagrams()) {
QByteArray byteArray;
byteArray = udpSocket->read(48);
ui->textEdit->append(QString::number(byteArray.size()));

QByteArray lastbyteArray(byteArray.right(8));

quint64 seconds = 0;
for (int i=0; i<=3; ++i)
{
seconds = (seconds << 8) | lastbyteArray[i];
}

ui->textEdit->append(QString::number(seconds, 10));

squidge
10th April 2010, 20:44
Each time you call udpSocket->read you need to append onto the end of your buffer. I don't think I need to describe how to do that :)

Secondly, there's a chance that readyRead occurs more than once, as you may not receive all the data in a single call, so your method needs to know how much data to expect, and only parse the data when it has received a full & valid packet.