原型QJsonObject ClientLogic::makeAuthedRequest(const QString& cmd) const
返回说明
返回带认证字段的 QJsonObject。
执行流程
- 创建 JSON 对象
- 写入命令和用户标识
- 写入 token
- 写入 session_id
关联接口
ClientLogic::hasValidAuthNetworkManager::sendJsonToServer
查看完整实现
clientlogic.cpp
QJsonObject ClientLogic::makeAuthedRequest(const QString& cmd) const
{
QJsonObject obj;
obj["cmd"] = cmd;
obj["from"] = currentUser;
obj["token"] = authToken;
obj["session_id"] = sessionId;
return obj;
}
原型void ClientLogic::requestLogin(const QString& user, const QString& pwd)
返回说明
无返回值;结果通过 sigLoginResult 信号返回。
执行流程
- 检查 pending 状态
- 校验输入
- 设置登录请求标记
- 构造 login 命令
- 发送请求
关联接口
ClientLogic::handleNetworkResponseBusinessHandler::handleLogin
查看完整实现
clientlogic.cpp
void ClientLogic::requestLogin(const QString& user, const QString& pwd)
{
stopHeartbeat();
currentUser.clear();
authToken.clear();
sessionId.clear();
if (user.isEmpty() || pwd.isEmpty()) {
emit sigLoginResult(false, "账号或密码不能为空");
return;
}
if (m_loginPending) {
emitUiLog("⚠️ 登录请求正在处理中,请勿重复点击");
return;
}
m_loginPending = true;
QJsonObject req;
req["cmd"] = "login";
req["user"] = user;
req["pwd"] = pwd;
netManager->sendJsonToServer(req);
}
原型void ClientLogic::startHeartbeat(int intervalSeconds)
调用时机
登录成功并获取 session_id 后调用。
参数
| 参数 | 说明 |
|---|
intervalSeconds | 心跳间隔秒数 |
执行流程
- 规范心跳间隔
- 创建或复用 QTimer
- 连接 timeout 信号
- 重置漏报计数
- 启动定时器
关联接口
ClientLogic::sendHeartbeatClientLogic::stopHeartbeatClientLogic::handleSessionInvalid
查看完整实现
clientlogic.cpp
void ClientLogic::startHeartbeat(int intervalSeconds)
{
if (!m_heartbeatTimer) {
return;
}
if (intervalSeconds <= 0) {
intervalSeconds = 10;
}
m_heartbeatIntervalMs = intervalSeconds * 1000;
m_missedHeartbeatCount = 0;
m_heartbeatTimer->stop();
m_heartbeatTimer->setInterval(m_heartbeatIntervalMs);
m_heartbeatTimer->start();
emitUiLog(QString("✅ 登录态心跳已启动,间隔 %1 秒").arg(intervalSeconds));
}
原型LoginSessionResult NetworkServer::createLoginSessionForUser(const std::string& username, const sockaddr_in& client_addr)
返回说明
返回 LoginSessionResult,包含 token、session_id 与是否替换旧会话。
参数
| 参数 | 说明 |
|---|
username | 用户标识 |
clientAddr | 本次登录来源地址 |
执行流程
- 生成随机 token/session
- 锁定会话表
- 判断旧会话
- 写入新会话与地址
- 返回会话结果
工程说明
同一账号的新会话会使旧 session 失效。
关联接口
NetworkServer::verifySessionNetworkServer::refreshHeartbeatNetworkServer::removeExpiredSessions
查看完整实现
network_server.cpp
LoginSessionResult NetworkServer::createLoginSessionForUser(const std::string& username,
const sockaddr_in& client_addr)
{
LoginSessionResult result;
if (!isValidUserId(username)) {
return result;
}
Session oldSession;
bool hasOldSession = false;
{
std::lock_guard<std::mutex> lock(online_mutex);
auto it = online_users.find(username);
if (it != online_users.end()) {
oldSession = it->second;
hasOldSession = !oldSession.ip.empty() && oldSession.port > 0;
}
}
result.token = makeRandomHexToken();
result.sessionId = "sess_" + makeRandomHexToken();
result.kickedOldSession = hasOldSession;
{
std::lock_guard<std::mutex> lock(token_mutex);
auto oldTokenIt = user_to_token.find(username);
if (oldTokenIt != user_to_token.end()) {
token_to_user.erase(oldTokenIt->second);
}
user_to_token[username] = result.token;
token_to_user[result.token] = username;
}
Session newSession = sessionFromAddr(client_addr);
newSession.token = result.token;
newSession.sessionId = result.sessionId;
newSession.loginTime = nowSeconds();
newSession.lastHeartbeat = newSession.loginTime;
{
std::lock_guard<std::mutex> lock(online_mutex);
online_users[username] = newSession;
}
if (hasOldSession) {
sockaddr_in oldAddr{};
oldAddr.sin_family = AF_INET;
if (inet_pton(AF_INET, oldSession.ip.c_str(), &oldAddr.sin_addr) == 1 &&
oldSession.port > 0 && oldSession.port <= 65535) {
oldAddr.sin_port = htons(static_cast<uint16_t>(oldSession.port));
std::string reason = "账号已在其他客户端登录,当前客户端已下线";
std::ostringstream oss;
oss << "{\"cmd\":\"force_logout\",\"reason\":\"" << reason << "\"}";
sendData(oss.str(), oldAddr);
}
}
return result;
}
原型void BusinessHandler::handleLogin(const std::string& json_str, struct sockaddr_in client_addr)
调用时机
dispatchTask 识别 login 命令后调用。
返回说明
无直接返回;通过 NetworkServer 回包。
参数
| 参数 | 说明 |
|---|
json_str | 客户端登录 JSON |
client_addr | 客户端 UDP 地址 |
执行流程
- 解析账号密码
- 调用 DBManager 校验
- 创建登录会话
- 组织 login_resp
- 发送响应
关联接口
DBManager::verifyLoginNetworkServer::createLoginSessionForUserClientLogic::handleNetworkResponse
查看完整实现
business_handler.cpp
void BusinessHandler::handleLogin(const std::string& json_str, struct sockaddr_in client_addr) {
try {
json j = json::parse(json_str);
std::string user = getStringField(j, "user");
std::string pwd = getStringField(j, "pwd");
json resp;
resp["cmd"] = "login_resp";
if (user.empty() || pwd.empty()) {
resp["result"] = "fail";
resp["msg"] = "账号或密码不能为空";
net_server->sendData(resp.dump(), client_addr);
return;
}
if (DBManager::getInstance().verifyLogin(user, pwd)) {
resp["result"] = "success";
resp["user"] = user;
std::string ip = inet_ntoa(client_addr.sin_addr);
int port = ntohs(client_addr.sin_port);
LoginSessionResult session =
net_server->createLoginSessionForUser(user, client_addr);
if (session.token.empty() || session.sessionId.empty()) {
resp["result"] = "fail";
resp["msg"] = "创建登录会话失败,请稍后重试";
net_server->sendData(resp.dump(), client_addr);
return;
}
resp["token"] = session.token;
resp["session_id"] = session.sessionId;
resp["heartbeat_interval"] = 10;
resp["session_timeout"] = 35;
resp["kicked_old_session"] = session.kickedOldSession;
std::cout << "✅ [用户登录] " << user
<< " 登录成功,当前地址: " << ip << ":" << port
<< ",session_id=" << session.sessionId
<< ",旧会话踢出=" << (session.kickedOldSession ? "yes" : "no")
<< "\n";
} else {
resp["result"] = "fail";
resp["msg"] = "账号不存在或密码错误";
std::cout << "❌ [登录拒绝] " << user << " 尝试登录失败\n";
}
net_server->sendData(resp.dump(), client_addr);
} catch (std::exception& e) {
std::cerr << "登录逻辑处理异常: " << e.what() << "\n";
}
}
原型bool BusinessHandler::checkAuth(const json& j, struct sockaddr_in client_addr, std::string& out_user)
返回说明
认证通过返回 true,否则返回 false。
参数
| 参数 | 说明 |
|---|
j | 业务 JSON 对象 |
client_addr | 请求来源地址 |
out_user | 认证成功后输出用户标识 |
执行流程
- 读取认证字段
- 校验字段完整性
- 验证 token/session
- 输出认证用户
- 必要时回传错误
关联接口
NetworkServer::verifySessionNetworkServer::verifyTokenClientLogic::handleSessionInvalid
查看完整实现
business_handler.cpp
bool BusinessHandler::checkAuth(const json& j,
struct sockaddr_in client_addr,
std::string& out_user)
{
out_user.clear();
json resp;
resp["cmd"] = "auth_failed";
std::string from = getStringField(j, "from");
std::string token = getStringField(j, "token");
std::string sessionId = getStringField(j, "session_id");
if (token.empty()) {
resp["reason"] = "缺少 token,请重新登录";
net_server->sendData(resp.dump(), client_addr);
return false;
}
if (sessionId.empty()) {
resp["reason"] = "缺少 session_id,请重新登录";
net_server->sendData(resp.dump(), client_addr);
return false;
}
if (from.empty()) {
resp["reason"] = "缺少 from 字段,请重新登录";
net_server->sendData(resp.dump(), client_addr);
return false;
}
if (!net_server || !net_server->verifySession(from, token, sessionId)) {
resp["reason"] = "登录状态已失效,请重新登录";
net_server->sendData(resp.dump(), client_addr);
return false;
}
net_server->refreshHeartbeat(from, token, sessionId, client_addr);
out_user = from;
return true;
}