Durch exakte Uhrzeit aus dem Internet die Systemuhr stellen lassen
-
Hi,
ich möchte ein Programm schreiben, das
1. sich aus dem Internet die aktuelle Uhrzeit holt und
2. die Systemzeit aktualisiert.
Ich weiß bloß nicht, wie ich Punkt 1 realisieren soll. Kann mir da einer helfen?
-
WebFritzi schrieb:
Hi,
ich möchte ein Programm schreiben, das
1. sich aus dem Internet die aktuelle Uhrzeit holt und
2. die Systemzeit aktualisiert.
Ich weiß bloß nicht, wie ich Punkt 1 realisieren soll. Kann mir da einer helfen?Hast Du den Petzold, da wird so ein Programm erstellt. Falls nicht dann google mal nach Time Protocol (RFC 868).
Ich poste mal hier wie der Ablauf in Petzold beschrieben ist:
1. Eine Verbindung mit Port 37 eines Servers herstellen, der diesen Dienst unterstützt
2. Auf einen 32-Bit-Wert warten, der die Zahl der seit dem 1.1.1900 um 0:00:00 Uhr verstrichene Sekunden angibt.
3. Die Verbindung beenden.
-
bevor du anfängst solltest du einen server finden der die richtige zeit anbietet.
-
Aha, mhm... Ich werds mal probieren...
-
Riskiere doch mal eine Blick auf http://www.ntp.org/ !
Ich weiß zwar nicht ob es ntptimeset, ntpd und Konsorten auch für Windows gibt, aber die protokollspezifischen Sachen kannst Du auf alle Fälle übernehmen bzw. viel von ihnen lernen...
-
Alternativ geht auch Port 13, aber da kommt ein String zurück. Zum Beispiel so (in Java):
import java.net.*; import java.io.*; public class Daytime { public static void main(String[] args) { try { Socket socket = new Socket ("www.uni-hamburg.de",13); InputStream stream = socket.getInputStream(); int len; byte [] bytes = new byte [50]; while ((len = stream.read(bytes)) != -1) { System.out.write(bytes,0,len); } stream.close(); socket.close(); System.out.println(); } catch (IOException e) { System.out.println (e); } } }
Ausgabe:
Thu Aug 7 23:39:06 2003
Klappt aber wie gesagt nicht mit jedem Host. Dieses Programm terminiert nichtmal, wenn du es mit einem falschen Host versuchst.
-
Mal eine ganz dumme Frage: Hat nicht sogar Windows mittlerweile einen eigenen NTP-Client an Board? Das ist nämlich eine technisch weit sauberere Lösung als diese daytime-Geschichte...
-
Naja, meine Synchronisation soll garnicht sooo genau sein. Ein paar Sekunden dürfen ruhig flöten gehen. Und alle Server (z.B. alle auf http://www.eecis.udel.edu/~mills/ntp/clock1a.html) geben die Zahl der seit dem 1.1.1900 um 0:00:00 Uhr verstrichenen Sekunden zurück, wenn man sie auf Port 37 anspricht, ja?
-
WebFritzi schrieb:
Naja, meine Synchronisation soll garnicht sooo genau sein. Ein paar Sekunden dürfen ruhig flöten gehen.
Dann brauchst Du nicht unbedingt NTP obwohl es natürlich trotzdem eine sehr elegante Lösung darstellt.
Und alle Server (z.B. alle auf http://www.eecis.udel.edu/~mills/ntp/clock1a.html) geben die Zahl der seit dem 1.1.1900 um 0:00:00 Uhr verstrichenen Sekunden zurück, wenn man sie auf Port 37 anspricht, ja?
Epochsekunden? Eigentlich glaube ich dass das ganze ein bisschen komplizierter abläuft aber ich habe die entsprechenden RFCs auch noch nicht gelesen. *g*
-
Since NTP timestamps are cherished data and, in fact, represent the main
product of the protocol, a special timestamp format has been
established. NTP timestamps are represented as a 64-bit unsigned fixed-
point number, in seconds relative to 0h on 1 January 1900. The integer
part is in the first 32 bits and the fraction part in the last 32 bits.
This format allows convenient multiple-precision arithmetic and
conversion to Time Protocol representation (seconds), but does
complicate the conversion to ICMP Timestamp message representation
(milliseconds). The precision of this representation is about 200
picoseconds, which should be adequate for even the most exotic
requirements.ftp://ftp.rfc-editor.org/in-notes/rfc1305.txt
-
Simpel aber nicht ganz so einfach zu automatisieren gibts auch noch das hier:
-
Sorry, habe mich geirrt, sind tatsächlich Sekunden seit 1900 und nicht Unix-Epoch + nochwas, wenn RFCs nur nicht manchmal so mühsam zu lesen wären...
edit: Hm, zu spät. Geistige Notiz an mich: Nicht mehr so lange Tabs offen lassen und mit dem Beantworten warten...
-
http://www.luckie-online.de/ -> InetTime.
-
Luckie schrieb:
http://www.luckie-online.de/ -> InetTime.
Wenn du auch noch sagen könntest, wie DU das bewerkstelligt hast, wäre das echt super.
P.S.: Komisch, ich will immer Programme schreiben, die du schon längst geschrieben hast. (siehe z.B. NetSend)
-
Ich kann dich beruhigen WebFritzi - es ist nicht leicht, was zufinden, was es nicht gibt. Ich glaub sogar Dönerbudensimulationen, Mückenpopulationsberechnung und eine Berechnung wieviel Toilettenpapier man in der Woche braucht ( in cm^2 ) gibts schon. Auch wenn ich selbst noch keine gesehen habe
-
WebFritzi schrieb:
Luckie schrieb:
http://www.luckie-online.de/ -> InetTime.
Wenn du auch noch sagen könntest, wie DU das bewerkstelligt hast, wäre das echt super.
Source ist doch dabei. Ist zwar Delphi-Language aber die API-Funktionen heißen genauso. Kommentiert ist der Source, glaube ich, auch noch.
P.S.: Komisch, ich will immer Programme schreiben, die du schon längst geschrieben hast. (siehe z.B. NetSend)
Einer ist immer schneller. :p
-
#ifndef TimeSyncH #define TimeSyncH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <NMDayTim.hpp> #include <Psock.hpp> #include <ExtCtrls.hpp> #include <Graphics.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // Von der IDE verwaltete Komponenten TNMDayTime *time; TButton *Button1; TButton *Button2; TEdit *Edit1; TImage *Image1; void __fastcall Button1Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); void __fastcall VerbFehler(TObject *Sender); void __fastcall WrongHost(bool &Handled); private: // Anwender-Deklarationen public: // Anwender-Deklarationen __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- #include <vcl.h> #include <dos.h> #pragma hdrstop #include "TimeSync.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; AnsiString output="Uhrzeit: "; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- /*Edit1->Text=time->DayTimeStr ; AnsiString tmp=Edit1->Text; int i=1; while(tmp[i++]!=' '); while(tmp[i++]!=' '); while(tmp[i++]!=' '); test->Text=Edit1->Text.SubString(i,8); struct time t; t.ti_hour=StrToInt(test->Text.SubString(1,2)); t.ti_min=StrToInt(test->Text.SubString(4,2)); t.ti_sec=StrToInt(test->Text.SubString(7,2)); settime(&t);*/ void __fastcall TForm1::Button1Click(TObject *Sender) { AnsiString tmp=time->DayTimeStr; int i=1; while(tmp[i++]!=' '); while(tmp[i++]!=' '); while(tmp[i++]!=' '); Edit1->Text=output+tmp.SubString(i,8); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { AnsiString tmp=time->DayTimeStr; int i=1; while(tmp[i++]!=' '); while(tmp[i++]!=' '); while(tmp[i++]!=' '); Edit1->Text=output+tmp.SubString(i,8); tmp=tmp.SubString(i,8); struct time t; t.ti_hour=StrToInt(tmp.SubString(1,2)); t.ti_min=StrToInt(tmp.SubString(4,2)); t.ti_sec=StrToInt(tmp.SubString(7,2)); settime(&t); } //--------------------------------------------------------------------------- void __fastcall TForm1::VerbFehler(TObject *Sender) { Edit1->Text="Verbindungsfehler"; } //--------------------------------------------------------------------------- void __fastcall TForm1::WrongHost(bool &Handled) { Edit1->Text="Host Fehler"; } //---------------------------------------------------------------------------
-
Bei mir (BCB3) gibt es kein TNMDayTime-Objekt. Ich würde das auch gerne auf LowLevel-Ebene über Sockets machen.
@unreg: Wo werden denn bitte die Funktionen "VerFehler" und "WrongHost" aufgerufen?
-
Meins ist low level.
-
Luckie schrieb:
Meins ist low level.
Und genau deswegen werd ich mir das auch mal anschauen. Sorry, ich wusst nicht, dass da auch der Code dabei ist.
-
Mehr Low-Level geht's nicht (außer asm):
/*------------------------------------------------------------------------ NETTIME.C - Zeit von Internet-Server erfragen und als Systemzeit setzen (c) Charles Petzold, 1998 -------------------------------------------------------------------------*/ #include <windows.h> #include "resource.h" #define WM_SOCKET_NOTIFY (WM_USER + 1) #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK MainDlg (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK ServerDlg (HWND, UINT, WPARAM, LPARAM) ; void ChangeSystemTime (HWND hwndEdit, ULONG ulTime) ; void FormatUpdatedTime (HWND hwndEdit, SYSTEMTIME * pstOld, SYSTEMTIME * pstNew) ; void EditPrintf (HWND hwndEdit, TCHAR * szFormat, ...) ; HINSTANCE hInst ; HWND hwndModeless ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("NetTime") ; HWND hwnd ; MSG msg ; RECT rect ; WNDCLASS wndclass ; hInst = hInstance ; wndclass.style = 0 ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = NULL ; wndclass.hbrBackground = NULL ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { // UNICODE-Kompilierung ist die einzige realistische Fehlermöglichkeit MessageBox (NULL, TEXT ("Programm arbeitet mit Unicode und setzt Windows NT voraus!"), szAppName, MB_ICONERROR) ; return 0 ; } // Programmfenster anlegen hwnd = CreateWindow (szAppName, TEXT ("Systemzeit aus Internet beziehen"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; // modusloses Dialogfeld anlegen hwndModeless = CreateDialog (hInstance, szAppName, hwnd, MainDlg) ; // Abmessungen des Hauptfensters denen des Dialogfelds anpassen // und beide Fenster anzeigen GetWindowRect (hwndModeless, &rect) ; AdjustWindowRect (&rect, WS_CAPTION | WS_BORDER, FALSE) ; SetWindowPos (hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE) ; ShowWindow (hwndModeless, SW_SHOW) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; // Nachrichtenschleife mit Erweiterung für das moduslose Dialogfeld while (GetMessage (&msg, NULL, 0, 0)) { if (hwndModeless == 0 || !IsDialogMessage (hwndModeless, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_SETFOCUS: SetFocus (hwndModeless) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK MainDlg (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static char szIPAddr[32] = { "132.163.135.130" } ; static HWND hwndButton, hwndEdit ; static SOCKET sock ; static struct sockaddr_in sa ; static TCHAR szOKLabel[32] ; int iError, iSize ; unsigned long ulTime ; WORD wEvent, wError ; WSADATA WSAData ; switch (message) { case WM_INITDIALOG: hwndButton = GetDlgItem (hwnd, IDOK) ; hwndEdit = GetDlgItem (hwnd, IDC_TEXTOUT) ; return TRUE ; case WM_COMMAND: switch (LOWORD (wParam)) { case IDC_SERVER: // Auswahl eines anderen Zeit-Servers DialogBoxParam (hInst, TEXT ("Servers"), hwnd, ServerDlg, (LPARAM) szIPAddr) ; return TRUE ; case IDOK: // WSAStartup aufrufen und Beschreibungstext anzeigen if (iError = WSAStartup (MAKEWORD(2,0), &WSAData)) { EditPrintf (hwndEdit, TEXT ("WSAStartup-Fehler #%i.\r\n"), iError) ; return TRUE ; } EditPrintf (hwndEdit, TEXT ("%hs gestartet.\r\n"), WSAData.szDescription); // Socket öffnen sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ; if (sock == INVALID_SOCKET) { EditPrintf (hwndEdit, TEXT ("Fehler beim Anlegen des Sockets: #%i.\r\n"), WSAGetLastError ()) ; WSACleanup () ; return TRUE ; } EditPrintf (hwndEdit, TEXT ("Socket %i angelegt.\r\n"), sock) ; // WSAAsyncSelect aufrufen if (SOCKET_ERROR == WSAAsyncSelect (sock, hwnd, WM_SOCKET_NOTIFY, FD_CONNECT | FD_READ)) { EditPrintf (hwndEdit, TEXT ("WSAAsyncSelect-Fehler #%i.\r\n"), WSAGetLastError ()) ; closesocket (sock) ; WSACleanup () ; return TRUE ; } // Aufruf von "connect" mit IP-Adresse und Portnummer des Zeitservers sa.sin_family = AF_INET ; sa.sin_port = htons (IPPORT_TIMESERVER) ; sa.sin_addr.S_un.S_addr = inet_addr (szIPAddr) ; connect(sock, (SOCKADDR *) &sa, sizeof (sa)) ; // "connect" wird wegen den vorherigen WSAAsyncSelect-Aufrufs SOCKET_ERROR liefern, // da die Funktion als potentiell blockierende Funktion die Kontrolle unmittelbar // (also noch vor dem Herstellen der Verbindung) zurückgibt. Der Statuscode // WSAEWOULDBLOCK ist deshalb nicht als Fehler zu betrachten. // Hier die Reaktion auf unwartete Fehler: if (WSAEWOULDBLOCK != (iError = WSAGetLastError ())) { EditPrintf (hwndEdit, TEXT ("Connect-Fehler #%i.\r\n"), iError) ; closesocket (sock) ; WSACleanup () ; return TRUE ; } EditPrintf (hwndEdit, TEXT ("Verbinde mit %hs..."), szIPAddr) ; // Das Ergebnis des "connect"-Aufrufs wird über WM_SOCKET_NOTIFY angeliefert. // Timer setzen und Schaltflächenbeschriftung auf "Abbrechen" ändern SetTimer (hwnd, ID_TIMER, 1000, NULL) ; GetWindowText (hwndButton, szOKLabel, sizeof (szOKLabel) / sizeof (TCHAR)) ; SetWindowText (hwndButton, TEXT ("Abbruch")) ; SetWindowLong (hwndButton, GWL_ID, IDCANCEL) ; return TRUE ; case IDCANCEL: closesocket (sock) ; sock = 0 ; WSACleanup () ; SetWindowText (hwndButton, szOKLabel) ; SetWindowLong (hwndButton, GWL_ID, IDOK) ; KillTimer (hwnd, ID_TIMER) ; EditPrintf (hwndEdit, TEXT ("\r\nSocket geschlossen.\r\n")) ; return TRUE ; case IDC_CLOSE: if (sock) SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ; DestroyWindow (GetParent (hwnd)) ; return TRUE ; } return FALSE ; case WM_TIMER: EditPrintf (hwndEdit, TEXT (".")) ; return TRUE ; case WM_SOCKET_NOTIFY: wEvent = WSAGETSELECTEVENT (lParam) ; // d.h. LOWORD wError = WSAGETSELECTERROR (lParam) ; // d.h. HIWORD // Behandlung zweier für WSAAsyncSelect spezifizierte Ereignisse switch (wEvent) { case FD_CONNECT: // Rückmeldung von connect() EditPrintf (hwndEdit, TEXT ("\r\n")) ; if (wError) { EditPrintf (hwndEdit, TEXT ("Connect Fehler #%i."), wError) ; SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ; return TRUE ; } EditPrintf (hwndEdit, TEXT ("Verbunden mit %hs.\r\n"), szIPAddr) ; // Versuche Daten zu empfangen. Der Aufruf wird einen WSAEWOULDBLOCK-Fehler // und ein FD_READ-Ereignis generieren. recv (sock, (char *) &ulTime, 4, MSG_PEEK) ; EditPrintf (hwndEdit, TEXT ("Warte auf Empfang...")) ; return TRUE ; case FD_READ: // Rückmeldung von recv() KillTimer (hwnd, ID_TIMER) ; EditPrintf (hwndEdit, TEXT ("\r\n")) ; if (wError) { EditPrintf (hwndEdit, TEXT ("FD_READ error #%i."), wError) ; SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ; return TRUE ; } // Zeit abfragen und Bytes vertauschen iSize = recv (sock, (char *) &ulTime, 4, 0) ; ulTime = ntohl (ulTime) ; EditPrintf (hwndEdit, TEXT ("Aktuelle Zeit ist: %u Sekunden ") TEXT ("seit 1.1.1900, 0:00 Uhr.\r\n"), ulTime) ; // Systemzeit setzen ChangeSystemTime (hwndEdit, ulTime) ; SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ; return TRUE ; } return FALSE ; } return FALSE ; } BOOL CALLBACK ServerDlg (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static char * szServer ; static WORD wServer = IDC_SERVER1 ; char szLabel [64] ; switch (message) { case WM_INITDIALOG: szServer = (char *) lParam ; CheckRadioButton (hwnd, IDC_SERVER1, IDC_SERVER10, wServer) ; return TRUE ; case WM_COMMAND: switch (LOWORD (wParam)) { case IDC_SERVER1: case IDC_SERVER2: case IDC_SERVER3: case IDC_SERVER4: case IDC_SERVER5: case IDC_SERVER6: case IDC_SERVER7: case IDC_SERVER8: case IDC_SERVER9: case IDC_SERVER10: wServer = LOWORD (wParam) ; return TRUE ; case IDOK: GetDlgItemTextA (hwnd, wServer, szLabel, sizeof (szLabel)) ; strtok (szLabel, "(") ; strcpy (szServer, strtok (NULL, ")")) ; EndDialog (hwnd, TRUE) ; return TRUE ; case IDCANCEL: EndDialog (hwnd, FALSE) ; return TRUE ; } break ; } return FALSE ; } void ChangeSystemTime (HWND hwndEdit, ULONG ulTime) { FILETIME ftNew ; LARGE_INTEGER li ; SYSTEMTIME stOld, stNew ; GetLocalTime (&stOld) ; stNew.wYear = 1900 ; stNew.wMonth = 1 ; stNew.wDay = 1 ; stNew.wHour = 0 ; stNew.wMinute = 0 ; stNew.wSecond = 0 ; stNew.wMilliseconds = 0 ; SystemTimeToFileTime (&stNew, &ftNew) ; li = * (LARGE_INTEGER *) &ftNew ; li.QuadPart += (LONGLONG) 10000000 * ulTime ; ftNew = * (FILETIME *) &li ; FileTimeToSystemTime (&ftNew, &stNew) ; if (SetSystemTime (&stNew)) { GetLocalTime (&stNew) ; FormatUpdatedTime (hwndEdit, &stOld, &stNew) ; } else EditPrintf (hwndEdit, TEXT ("Konnte Systemzeit nicht ändern.")) ; } void FormatUpdatedTime (HWND hwndEdit, SYSTEMTIME * pstOld, SYSTEMTIME * pstNew) { TCHAR szDateOld [64], szTimeOld [64], szDateNew [64], szTimeNew [64] ; GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE, pstOld, NULL, szDateOld, sizeof (szDateOld)) ; GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, pstOld, NULL, szTimeOld, sizeof (szTimeOld)) ; GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE, pstNew, NULL, szDateNew, sizeof (szDateNew)) ; GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, pstNew, NULL, szTimeNew, sizeof (szTimeNew)) ; EditPrintf (hwndEdit, TEXT ("Systemdatum und Systemzeit wurden von") TEXT ("\r\n\t%s, %s.%03i auf\r\n\t%s, %s.%03i korrigiert."), szDateOld, szTimeOld, pstOld->wMilliseconds, szDateNew, szTimeNew, pstNew->wMilliseconds) ; } void EditPrintf (HWND hwndEdit, TCHAR * szFormat, ...) { TCHAR szBuffer [1024] ; va_list pArgList ; va_start (pArgList, szFormat) ; wvsprintf (szBuffer, szFormat, pArgList) ; va_end (pArgList) ; SendMessage (hwndEdit, EM_SETSEL, (WPARAM) -1, (LPARAM) -1) ; SendMessage (hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) szBuffer) ; SendMessage (hwndEdit, EM_SCROLLCARET, 0, 0) ; }