diff --git a/server/2015Remote/McpServer.cpp b/server/2015Remote/McpServer.cpp index 95d1f9c..461c45a 100644 --- a/server/2015Remote/McpServer.cpp +++ b/server/2015Remote/McpServer.cpp @@ -3193,6 +3193,11 @@ int CMcpServer::CloseScreenCtrlSession(uint64_t device_id, const std::string& se auto it = m_ScreenCtrlSessions.find(device_id); if (it == m_ScreenCtrlSessions.end()) return 1; // 不存在(幂等) if (it->second.sessionId != sessionId) return 2; // token 不匹配 + if (it->second.busy) { + // 注入在飞:置 closed 交注入线程收尾,避免与注入并发擦会话(镜像终端 busy 模式)。 + it->second.closed = true; + return 0; + } if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx); m_ScreenCtrlSessions.erase(it); return 0; @@ -3205,7 +3210,7 @@ int CMcpServer::SweepIdleScreenCtrl(time_t idleTimeoutSec) { std::lock_guard lk(m_ScreenCtrlMutex); for (auto it = m_ScreenCtrlSessions.begin(); it != m_ScreenCtrlSessions.end(); ) { ScreenCtrlSession& s = it->second; - if (difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) { + if (!s.busy && difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) { if (s.subCtx) m_ScreenCtrlContextToDevice.erase(s.subCtx); toClose.push_back(it->first); it = m_ScreenCtrlSessions.erase(it); @@ -3226,8 +3231,49 @@ void CMcpServer::OnScreenControlClosed(context* subCtx) { std::lock_guard lk(m_ScreenCtrlMutex); auto it = m_ScreenCtrlContextToDevice.find(subCtx); if (it == m_ScreenCtrlContextToDevice.end()) return; - m_ScreenCtrlSessions.erase(it->second); - m_ScreenCtrlContextToDevice.erase(it); + auto sit = m_ScreenCtrlSessions.find(it->second); + if (sit == m_ScreenCtrlSessions.end()) { // 路由在但会话已擦(防御) + m_ScreenCtrlContextToDevice.erase(it); + return; + } + if (sit->second.busy) { + // 注入在飞:不擦会话(注入线程仍持 subCtx),仅置 closed,由 EndScreenCtrlAction 收尾。 + sit->second.closed = true; + } else { + m_ScreenCtrlSessions.erase(sit); + m_ScreenCtrlContextToDevice.erase(it); + } +} + +int CMcpServer::BeginScreenCtrlAction(uint64_t device_id, const std::string& sessionId, + context*& subCtx, int& screenW, int& screenH) { + std::lock_guard lk(m_ScreenCtrlMutex); + auto it = m_ScreenCtrlSessions.find(device_id); + if (it == m_ScreenCtrlSessions.end()) return 1; // 会话不存在 + ScreenCtrlSession& s = it->second; + if (s.sessionId != sessionId) return 1; // token 不匹配 + if (!s.started) return 1; // 未就绪 + if (s.busy) return 2; // 已有注入在飞 + if (s.closed) return 1; // 子连接已断 + s.busy = true; + s.lastActiveAt = time(nullptr); + subCtx = s.subCtx; + screenW = s.screenW; + screenH = s.screenH; + return 0; +} + +void CMcpServer::EndScreenCtrlAction(uint64_t device_id, const std::string& sessionId) { + std::lock_guard lk(m_ScreenCtrlMutex); + auto it = m_ScreenCtrlSessions.find(device_id); + if (it == m_ScreenCtrlSessions.end()) return; + if (it->second.sessionId != sessionId) return; + it->second.busy = false; + it->second.lastActiveAt = time(nullptr); + if (it->second.closed) { // 注入期间子连接已断:擦会话+路由 + if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx); + m_ScreenCtrlSessions.erase(it); + } } ////////////////////////////////////////////////////////////////////////// diff --git a/server/2015Remote/McpServer.h b/server/2015Remote/McpServer.h index 928f19c..57c27c7 100644 --- a/server/2015Remote/McpServer.h +++ b/server/2015Remote/McpServer.h @@ -175,6 +175,15 @@ public: bool MarkScreenCtrlReady(uint64_t device_id, const std::string& sessionId, context* subCtx, int screenW, int screenH); + // 注入前登记(校验 sessionId/started/!busy/!closed)。输出 subCtx/screenW/H。 + // 注入期间 busy=true,保证 close/sweep/断线不与注入并发擦会话(镜像终端 busy 模式, + // 评审发现 #6)。返回:0=ok;1=不存在/不匹配/未就绪/已断(-32002);2=忙(-32003)。 + int BeginScreenCtrlAction(uint64_t device_id, const std::string& sessionId, + context*& subCtx, int& screenW, int& screenH); + + // 注入后复位 busy;若注入期间屏幕子连接已断(closed),擦会话+路由(防泄漏)。 + void EndScreenCtrlAction(uint64_t device_id, const std::string& sessionId); + // 关闭控制会话(校验 sessionId;擦路由 + 会话)。屏幕子连接的关闭由调用方锁外执行 // (CloseWebRemoteDesktopByClientID)。返回 0=已关;1=不存在(幂等);2=sessionId 不匹配。 int CloseScreenCtrlSession(uint64_t device_id, const std::string& sessionId); @@ -253,6 +262,8 @@ private: std::string sessionId; // 会话 token(每次 open 独立随机) context* subCtx = nullptr; // 屏幕子连接上下文(就绪后填;用于注入) bool started = false; // false=子连接建立中;true=已就绪 + bool busy = false; // 一条注入在飞(BeginScreenCtrlAction→End 之间) + bool closed = false; // 注入期间屏幕子连接已断(由注入线程收尾) time_t lastActiveAt = 0; // idle 回收用(秒) int screenW = 0; // 物理捕获分辨率(来自 TOKEN_BITMAPINFO) int screenH = 0;