Zum Verbinden mit dem Server erstellen wir zunächst ein Socket:
s := socket(AF_INET,SOCK_STREAM,0);
Der erste Parameter ist die Angabe der Protokollfamilie. AF_INET schließt hierbei TCP, UDP u.ä. ein. Der zweite Parameter definiert die Art der Verbindung, wobei SOCK_STREAM hier für eine TCP-Verbindung steht.
Im Fehlerfall ist das Ergebnis der Funktion INVALID_SOCKET, wobei sich dann mit "WSAGetLastError" die genaue Fehlerursache herausfinden lässt.
Konnte das Socket erstellt werden, benötigen wir eine TSockAddr-Variable, der wie den Port und die IP-Adresse des Servers übergeben. Und damit sind wir schon beim "time protocol":
Üblicherweise verbinden wir uns mit dem Server auf Port 37. Ist das geschehen, liefert uns der Server einen Zeitwert (32 Bit) zurück und beendet die Verbindung. Der übermittelte Wert entspricht der Anzahl der Sekunden seit dem 1. Januar 1900, Null Uhr.
Wie gehen wir nun vor?
Zunächst die schon erwähnte TSockAddr-Variable:
saddr.sin_family := AF_INET; saddr.sin_addr.S_addr := dwIp; // IP-Adresse des Servers (host byte order!) saddr.sin_port := htons(37);
Im nächsten Schritt verbinden wir uns mit dem Server. Dazu dient die Funktion "connect", die neben dem Socket auch die eben gefüllte TSockAddr-Variable und ihre Größe als Parameter erwartet:
res := connect(s,saddr,sizeof(TSockAddr));
Im Erfolgsfall lesen wir dann mit Hilfe der Funktion "recv" den Zeitwert in eine dword-Variable. Die Funktion erwartet natürlich wieder die Socket-Variable, gefolgt von der dword-Variable für den Zeitwert und ihrer Größe. Die Flags im letzten Parameter setzen wir auf Null:
if(res <> SOCKET_ERROR) then
res := recv(s,iTime,sizeof(iTime),0);
Der ausgelesene Wert liegt nun aber in der "host byte order" vor. Bevor wir ihn verwenden können, müssen wir ihn mit Hilfe von "htonl" "drehen":
iTime := htonl(iTime);
Sie können diesen Schritt gern einmal auslassen. ;o)
Es wurde bereits gesagt, dass sich der Zeitwert auf den 1. Januar 1900, Null Uhr, bezieht. Wir müssen also dieses Datum zu dem Wert addieren, den wir vom Server bekommen haben. Dazu benutzen wir zunächst eine TSystemTime-Variable, die wir entsprechend einstellen
st.wYear := 1900; st.wMonth := 1; st.wDayOfWeek := 0; st.wDay := 1; st.wHour := 0; st.wMinute := 0; st.wSecond := 0; st.wMilliseconds := 0;
und mit der Funktion
FILETIME-Wert umrechnen
if(not SystemTimeToFileTime(st,ft)) then goto SocketClose;
Nun können wir die beides addieren. Der Zeitwert vom Server muss allerdings als int64 übergeben und mit 10.000.000 (10 Millionen) multipliziert werden:
li.QuadPart := (int64(iTime) * 10000000) +
ULARGE_INTEGER(ft).QuadPart;
Warum?
FILETIME ist ein 64-Bit-Integer, der die Anzahl von 100ns-Intervallen (Nanosekunden) pro Sekunde seit dem 1. Januar 1601 wiedergibt (UTC). Der 1. Januar 1900, den wir mit "SystemTimeToFileTime" umgerechnet haben, enthält bereits die Anzahl dieser Intervalle. Nur der Server-Wert muss noch entsprechend ergänzt werden.
Und der Wert von 10.000.000 ergibt sich, weil pro Sekunde eben 10 Millionen solcher 100ns-Intervalle möglich sind:
1sec = 1.000 msec = 1.000.000 µsec = 1.000.000.000 ns
Der so ermittelte Wert kann nun mit der Funktion
TSystemTime-Variable umgewandelt werden und sollte dann eigentlich die gültige Zeit und das gültige Datum enthalten.
if(not FileTimeToSystemTime(TFileTime(li),st)) then goto SocketClose;