Получение точного времени

Получение точного времени по протоколу NTP/SNTP

Платформа: Windows
Автор: ShadEx

#INCLUDE Once "win/winsock2.bi"

Function get_NTP_sec(ntpServ As Zstring, ntpPort As Integer = 123, cu_sec As Byte = 0) As Uinteger
    If(Len(ntpServ) < 5 Or Instr(ntpServ, ".") = 0) Then
        ntpServ = "pool.ntp.org" '"193.27.209.1"
    Endif
    Dim tdiff_ns2us As Uinteger = 2208988800

    Dim wsa_ptr As WSAData

    If WSAStartup(MAKEWORD(2, 0), @wsa_ptr) = SOCKET_ERROR Then
        Return 0
    End If

    If(wsa_ptr.wVersion <> MAKEWORD(2, 0)) Then
        WSACleanup()
        Return 0
    End If

    Dim sock As socket

    sock = OpenSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    If(sock = INVALID_SOCKET) Then
        WSACleanup()
        Return 0
    Endif

    'Dim Bcast As Bool = TRUE
    'setsockopt(sock,SOL_SOCKET,SO_BROADCAST,@Bcast,sizeof(BOOL))

    Dim ia As in_addr
    Dim hostentry As hostent Ptr
    Dim ip_iaddr As u_long

    ia.s_addr = inet_addr(Strptr(ntpServ))

    If (ia.s_addr = INADDR_NONE Or ia.s_addr = 0) Then
        hostentry = gethostbyname(ntpServ)
        If (hostentry = 0) Then
            WSACleanup()
            Return 0
        End If
        ip_iaddr = *Cast(Uinteger Ptr, *hostentry->h_addr_list)
    Else
        ip_iaddr = ia.s_addr
    End If

    Dim saddr As sockaddr_in
    saddr.sin_family = AF_INET
    saddr.sin_port    = htons(ntpPort)
    saddr.sin_addr.s_addr =  ip_iaddr 'inet_addr(ntpServ)

    If(connect(sock, Cast(PSOCKADDR, @saddr), Len(saddr)) = SOCKET_ERROR) Then
        'Var WSA_err = WSAGetLastError()
        '? "WSA_err = " &  WSA_Err
        closesocket(sock)
        WSACleanup()
        Return 0
    Endif

    Dim ps_buff As Ubyte Ptr = Callocate(68, Sizeof(Byte))
    ps_buff[0] = &h1B

    If(send(sock, ps_buff, 68, 0) <= 0) Then
        closesocket(sock)
        WSACleanup()
        Return 0
    End If
    Dim pr_buff As Ubyte Ptr = Callocate(68, Sizeof(Byte))
    Dim rcv_len As Integer = 255
    Dim rcv_bytes As Integer = recv(sock, pr_buff, rcv_len, 0)
    If(rcv_bytes < 44 Or Cast(Ubyte,pr_buff[40]) < 127) Then
        closesocket(sock)
        WSACleanup()
        Return 0
    End If

    Dim res_sec As Uinteger = 0

    res_sec  = pr_buff[40] Shl 24
    res_sec += pr_buff[41] Shl 16
    res_sec += pr_buff[42] Shl 8
    res_sec += pr_buff[43] Shl 0

    If(res_sec <= tdiff_ns2us) Then
        Return 0
    Endif
    If(cu_sec <> 0) Then
        Return (res_sec - tdiff_ns2us)
    Endif

    Return res_sec
End Function

#INCLUDE Once "crt/time.bi"

Shell "chcp 1251"

Var curr_sec = get_NTP_sec("",,1)

If(curr_sec > 0) Then
    Var tcurr_sec = Cast(time_t,curr_sec)
    Var ptime_GM = gmtime(@tcurr_sec)
    Dim As Zstring * 255 tstr_GM
    Var res=strftime(tstr_GM,128,"%a, %d %b %Y %H:%M:%S",ptime_GM)
    Print "Точное время по Гринвичу: " & tstr_GM

    curr_sec += (3600 * 4)
    Var tcurr_sec_4h = Cast(time_t,curr_sec)
    Var ptime_MSK = gmtime(@tcurr_sec_4h)
    Dim As Zstring * 255 tstr_MSK
    res=strftime(tstr_MSK,128,"%a, %d %b %Y %H:%M:%S",ptime_MSK)
    Print "Точное время по Москве:   " & tstr_MSK
Else
    Print "Произошла ошибка при получении временой метки..."
Endif

Sleep