cpp
//定时时间同步
QTimer *m_pTimeSync = nullptr;
#ifdef Q_OS_WIN
#include <windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#endif
其余头文件未写出(网络通信等)
m_pTimeSync = new QTimer();
connect(m_pTimeSync, &QTimer::timeout, this, &SysMonitor::syncTimeFromServer);
m_pTimeSync->start(nnTimeMinute * 60 * 1000);
//具体实现
void syncTimeFromServer()
{
QTcpSocket socket;
socket.connectToHost(m_strTimeServerIP, 37);
if (!socket.waitForConnected(3000))
{
LogHelper::error("[syncTimeFromServer] tcpNetError: " + socket.errorString());
return ;
}
// 第二步:读取时间数据(RFC 868 协议,4字节大端整数)
if (!socket.waitForReadyRead(3000))
{
LogHelper::error("[syncTimeFromServer] tcpReadTimeOut!");
return ;
}
QByteArray data = socket.readAll();
if (data.size() != 4)
{
LogHelper::error("[syncTimeFromServer] tcpReadDateError!");
return ;
}
// 第三步:解析时间戳(大端序)
quint32 networkTime = 0;
networkTime |= (unsigned char)data[0] << 24;
networkTime |= (unsigned char)data[1] << 16;
networkTime |= (unsigned char)data[2] << 8;
networkTime |= (unsigned char)data[3];
// RFC 868 时间戳(1900-01-01 起)转 Unix 时间戳(1970-01-01 起)
if (networkTime < 2208988800UL)
{
LogHelper::error("[syncTimeFromServer] Invalid network time!");
return;
}
qint64 unixTime = static_cast<qint64>(networkTime) - 2208988800LL;
LogHelper::debug("[syncTimeFromServer] getTime: " + QString::number(unixTime));
// 第四步:设置系统时间(需要 root 权限)
#ifdef Q_OS_WIN
// ---------- Windows 平台 ----------
// 1. 启用 SE_SYSTEMTIME_NAME 特权
HANDLE hToken = nullptr;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
LogHelper::error("[syncTimeFromServer] OpenProcessToken error: "
+ QString::number(GetLastError()));
return;
}
LUID luid;
if (!LookupPrivilegeValue(nullptr, SE_SYSTEMTIME_NAME, &luid))
{
LogHelper::error("[syncTimeFromServer] LookupPrivilegeValue error: "
+ QString::number(GetLastError()));
CloseHandle(hToken);
return;
}
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr))
{
LogHelper::error("[syncTimeFromServer] AdjustTokenPrivileges error: "
+ QString::number(GetLastError()));
CloseHandle(hToken);
return;
}
// 注意:AdjustTokenPrivileges 返回 TRUE 也可能是失败
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
LogHelper::error("[syncTimeFromServer] 当前进程没有 SE_SYSTEMTIME_NAME 特权,请以管理员身份运行");
CloseHandle(hToken);
return;
}
CloseHandle(hToken);
// 2. Unix 时间戳 -> SYSTEMTIME (UTC)
// unixTime 是秒数,先转成 FILETIME 更方便
// FILETIME 起点:1601-01-01 00:00:00 UTC
// Unix 起点:1970-01-01 00:00:00 UTC
// 两者相差 11644473600 秒
ULARGE_INTEGER ull;
ull.QuadPart = (static_cast<ULONGLONG>(unixTime) + 11644473600ULL) * 10000000ULL; // 100ns 单位
FILETIME ft;
ft.dwLowDateTime = ull.LowPart;
ft.dwHighDateTime = ull.HighPart;
SYSTEMTIME st;
if (!FileTimeToSystemTime(&ft, &st))
{
LogHelper::error("[syncTimeFromServer] FileTimeToSystemTime error: "
+ QString::number(GetLastError()));
return;
}
// 3. 设置 UTC 系统时间(与 Linux settimeofday 语义一致)
if (SetSystemTime(&st))
{
LogHelper::debug("[syncTimeFromServer] SetSystemTime successful!");
}
else
{
LogHelper::error("[syncTimeFromServer] SetSystemTime error: "
+ QString::number(GetLastError()));
}
#else
struct timeval tv;
tv.tv_sec = static_cast<time_t>(unixTime);
tv.tv_usec = 0;
if (settimeofday(&tv, nullptr) == 0)
{
LogHelper::debug("[syncTimeFromServer] settimeofday sccessful!");
}
else
{
LogHelper::error("[syncTimeFromServer] settimeofday error!" );
perror("settimeofday");
}
#endif
}
cpp
A.确保服务器的时间服务开启
1.安装
sudo rpm -ivh xinetd-2.3.15-35.ky10.x86_64.rpm
2.编辑文件
sudo vim /etc/xinetd.d/time
3.
service time
{
disable = no
type = INTERNAL
id = time-stream
socket_type = stream
protocol = tcp
user = root
wait = no
}
4.执行重启
sudo systemctl restart xinetd
5.检查端口:
time 服务使用 TCP 37 端口,可以用 netstat -tlnp | grep 37 或 ss -tlnp | grep 37 检查是否处于监听状态
6.开启防火墙需要放行37端口
sudo firewall-cmd --permanent --add-port=37/tcp
sudo firewall-cmd --reload
7.建议确认它已设为开机自启:
sudo systemctl enable xinetd
sudo systemctl status xinetd # 确认状态是 active (running)
安装包: