试图使用托管C++实现NTP客户端,但使用time.windows.com获取1899年的日期
Trying to implement NTP Client using Managed C++ but getting date from 1899 using time.windows.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);
}
这在很大程度上是一个猜测,但是:
intPart * 1000
自1900年以来,它已经过去了大约38亿秒。为了存储该数字,使用ulong的所有32位。然后你乘以1000,不需要先上施法。这将结果保持为32位整数,但它会溢出,因此数据会被销毁。
若要修复此问题,请先强制转换为64位整数,然后再转换为毫秒。或者不用麻烦转换,直接调用AddSeconds即可。
也就是说,你可能有网络问题,结果是全零,那将是1900年1月1日,但随后你将转换为东部标准时间,这是一个5小时的偏移量,也就是1899年12月31日下午7:00。
David 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));
}
};
相关文章:
- Windows (COM) API 的行为不同,没有特定的库
- 由非托管(C++)COM服务器实例化的托管(C#)控件在Windows更新后损坏
- Windows COM C++ - 使用删除或释放
- 如何在Windows中注册COM(组件对象模型)?(用于图标覆盖处理程序)
- 连接到Windows 7上10的COM端口时,无效的手柄错误
- 识别与 Windows USB 虚拟 COM 端口的连接事件
- 无法在COM端口上正确使用windows.h ReadFile.WriteFile似乎工作正常
- 我可以在Qt应用程序中使用现有的本机Windows COM接口吗
- COM in the non-Windows world?
- 使用COM在Windows上的本地计算机上进行进程间通信
- 在C#中为windows API编写COM服务器,从何开始
- Com端口:Windows 8下的串口通信
- ActiveQt Com应用程序示例- Com服务器未在Windows注册表中注册(Qt4.7.4)
- 在Windows中向COM端口写入位-相当于INPORTB函数
- Windows传感器API C++COM集成的方向传感器没有在Windows 10上定期更新
- 我可以通过Windows服务访问ititon.com界面吗?
- COM服务器作为windows服务,不能将数组作为函数参数传递
- printf from Nucleo to Windows-7 virtual COM 在 X-NUCLEO-IKS01A1 示例中不起作用
- 我可以在服务中有一个COM接口从Windows脚本主机调用
- 以编程方式删除Windows中的COM端口(安装API?)