我想在 Java 中测量经过的时间。然而,System.currentTimeMillis()
和(我相信)的差异System.nanoTime()
可以通过外部变化来改变,例如某人(或系统)改变系统时钟。
使用网络调用不是一种选择,因为它可能需要非常频繁和快速的返回。
有一个通用的解决方案吗?
编辑
对不起,我应该详细说明原因。这不是为了阻止恶意用户 - 它是诸如客户端因空闲和常规客户端事件而发起的注销之类的事情。
我不认为有办法做到这一点。
肯定没有不能颠覆的办法。从根本上说,您受操作系统和 JVM 的支配,以决定向 Java 应用程序报告的当前时间。可以修补其中一个或两个,以便 Java 代码最终得到一个虚假的时间戳值。您可以尝试对此进行防御,但黑客需要做的就是修补您的应用程序以完全禁用许可证检查。
作为记录,无论您是否使用 Java,这个“漏洞”都适用。
这并不能真正回答您的问题,但错误 #6458294暗示 Sun 的 nanoTime() 实现将尽可能使用真正单调的机制(Linux 上的 CLOCK_MONOTONIC,Windows 上的 QueryPerformanceFrequency/QueryPerformanceCounter)。只有当这些不可用时,它才会退回到易受系统时钟变化影响的机制。
如果您可以控制(或至少了解)您正在运行的硬件,并且可以确保这些时钟机制可用,那么您可能会很幸运,并且 nanoTime() 会做得很好。
您可能还想阅读这篇博文,其中更详细地讨论了 HotSpot-on-Windows 案例。
如果您试图通过设置时钟来阻止人们破坏许可方案,您需要将迄今为止您看到的最长时间存储在某种加密和安全的存储中,然后在任何时间都禁用该程序低于你见过的最高值(当然还有一些 NTP 时钟调整和 DST 更改的余量),或者它们是否以某种方式篡改或删除了安全存储。
如果系统时钟发生变化,我不知道 nanoTime() 是否可能会发生变化,但我想这是可能的。nanoTime() 也可能不准确。
如果你真的需要防止时钟变化,你可以在一个线程中监控时钟。休眠 100 毫秒或 1000 毫秒,然后调用 currentTimeMillis()。如果时钟前进了超过 1000 + x 或已经倒退,那么时钟很可能发生了变化(或者线程被挂断了,这是可能的)。
如果发生分离,您实际上可以进行网络调用进行检查。当然,网络时间可能会因为闰秒的插入而改变。我曾经读过一些科学家的 Slashdot 评论,他们正在协调来自许多不同来源的天文数据。在他的实验过程中添加了闰秒,它基本上毁了它,因为有些网站插入了它,而另一些则没有。
另一种可能性可能是使用低级别的本机 API 来获取其他一些系统计时器。例如校准 API 的系统或网络正常运行时间。Windows 具有 getTickCount() 函数,该函数返回自启动以来的毫秒数。在 unix 系统上,您可以使用 uptime 命令进行粗略估计。您可以定期检查这些值以查看系统时钟是否已更改。
如果您不介意向 Java 应用程序添加一些本机代码,请使用:
QueryPerformanceCounter()
QueryPerformanceFrequency()
在 Windows 上;或者
带有时钟 ID 的POSIXclock_gettime()
函数。CLOCK_MONOTONIC
请注意,不鼓励在多处理器系统上使用 x86 TSC 寄存器,因此您最好使用上述 API。