尝试使用Managed C++实现NTP客户端,但使用www.example.com获取1899年之后的日期time.windows.com

xoshrz7s  于 2022-11-19  发布在  Windows
关注(0)|答案(2)|浏览(126)

我试图实现一个代码来从www.example.com获取时间time.windows.com但是它返回了一个奇怪的日期(我得到的日期是1899年)。由于相同的服务器使用WinSock为我的非托管C++代码工作,我可以想象我的代码本身一定有问题。有人能看看下面的代码,告诉我哪里做错了吗?

typedef unsigned int uint;
typedef unsigned long ulong;

long GetTimestampFromServer()
{
    System::String^ server = L"time.windows.com";

    array<unsigned char>^ ntpData = gcnew array<unsigned char>(48);
    ntpData[0] = 0x1B;

    array<System::Net::IPAddress^>^ addresses = System::Net::Dns::GetHostEntry(server)->AddressList;
    System::Net::IPEndPoint^ ipEndPoint = gcnew System::Net::IPEndPoint(addresses[0], 123);

    System::Net::Sockets::Socket^ socket = gcnew System::Net::Sockets::Socket
    (
        System::Net::Sockets::AddressFamily::InterNetwork,
        System::Net::Sockets::SocketType::Dgram,
        System::Net::Sockets::ProtocolType::Udp
    );

    try
    {
        socket->Connect(ipEndPoint);
        socket->ReceiveTimeout = 3000;
        socket->Send(ntpData);
        socket->Receive(ntpData);
        socket->Close();
    }
    catch (System::Exception^ e)
    {
        System::Console::WriteLine(e->Message);
        return 0;
    }

    const System::Byte serverReplyTime = 40;
    ulong intPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime);
    ulong fractPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime + 4);

    intPart = SwapEndianness(intPart);
    fractPart = SwapEndianness(fractPart);

    long long milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);

    System::DateTime networkDateTime = (gcnew System::DateTime(1900, 1, 1, 0, 0, 0, System::DateTimeKind::Utc))->AddMilliseconds((long)milliseconds);

    std::cout << ConvertToTimestamp(networkDateTime);

    return 0;
}

static uint SwapEndianness(ulong x)
{
    return (uint)(((x & 0x000000ff) << 24) +
        ((x & 0x0000ff00) << 8) +
        ((x & 0x00ff0000) >> 8) +
        ((x & 0xff000000) >> 24));
}

long ConvertToTimestamp(System::DateTime value)
{
    System::TimeZoneInfo^ NYTimeZone = System::TimeZoneInfo::FindSystemTimeZoneById(L"Eastern Standard Time");
    System::DateTime NyTime = System::TimeZoneInfo::ConvertTime(value, NYTimeZone);
    System::TimeZone^ localZone = System::TimeZone::CurrentTimeZone;
    System::Globalization::DaylightTime^ dst = localZone->GetDaylightChanges(NyTime.Year);
    NyTime = NyTime.AddHours(-1);
    System::DateTime epoch = System::DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime();
    System::TimeSpan span = (NyTime - epoch);
    return (long)System::Convert::ToDouble(span.TotalSeconds);
}
b1zrtrql

b1zrtrql1#

这在很大程度上是一种猜测,但是:

intPart * 1000

从1900年到现在已经有38亿秒了。为了存储这个数字,我们使用了ulong的所有32位。然后你乘以1000,而不是先进行上转换。这会使结果保持为32位整数,但是它溢出了,所以你的数据被破坏了。
若要修正,请先转换成64比特整数,再转换成毫秒。或者不需要转换,直接呼叫AddSeconds即可。
也就是说,你可能有一个网络问题,得到的结果全是零,这将是1900年1月1日,但然后你转换到东部标准时间,这是一个5小时的偏移,这将是1899年12月31日下午7:00。

btqmn9zl

btqmn9zl2#

大卫Yaw把我推向了正确的方向。非常感谢他,我终于让我的代码工作起来了。在整个方法中,有很多与不同语句相关的错误。我已经修复了它们,并在下面发布了新代码。

ref class SNTP
{
public: static DateTime GetNetworkTime()
{
    System::String^ ntpServer = "time.windows.com";

    auto ntpData = gcnew cli::array<unsigned char>(48);
    ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)

    auto addresses = System::Net::Dns::GetHostEntry(ntpServer)->AddressList;
    auto ipEndPoint = gcnew System::Net::IPEndPoint(addresses[0], 123);

    auto socket = gcnew System::Net::Sockets::Socket
    (
        System::Net::Sockets::AddressFamily::InterNetwork,
        System::Net::Sockets::SocketType::Dgram,
        System::Net::Sockets::ProtocolType::Udp
    );

    socket->Connect(ipEndPoint);
    socket->ReceiveTimeout = 3000;
    socket->Send(ntpData);
    socket->Receive(ntpData);
    socket->Close();

    const System::Byte serverReplyTime = 40;

    System::UInt64 intPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime);
    System::UInt64 fractPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime + 4);

    intPart = SwapEndianness(intPart);
    fractPart = SwapEndianness(fractPart);

    auto milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);

    auto networkDateTime = (System::DateTime(1900, 1, 1, 0, 0, 0, System::DateTimeKind::Utc)).AddMilliseconds((System::Int64)milliseconds);

    return networkDateTime.ToLocalTime();
}
private: static System::UInt32 SwapEndianness(System::UInt64 x)
{
    return (System::UInt32)(((x & 0x000000ff) << 24) +
        ((x & 0x0000ff00) << 8) +
        ((x & 0x00ff0000) >> 8) +
        ((x & 0xff000000) >> 24));
}
};

相关问题