fix: resolve connection failure when join game UI is opened prematurely

This commit is contained in:
2026-01-05 19:53:09 +08:00
parent 432f6ecc85
commit 291074c2b5
3 changed files with 48 additions and 14 deletions

View File

@@ -3,7 +3,8 @@
Client::Client(asio::io_context& ioContext): Client::Client(asio::io_context& ioContext):
m_ioContext(ioContext), m_ioContext(ioContext),
m_resolver(ioContext), m_resolver(ioContext),
m_socket(ioContext) m_socket(ioContext),
m_reconnectTimer(ioContext)
{ {
// 构造函数实现 // 构造函数实现
} }
@@ -24,6 +25,8 @@ void Client::connect(const std::string& host, int port, bool iAmFirst) {
m_port = port; m_port = port;
m_isHost = iAmFirst; m_isHost = iAmFirst;
m_shouldWait = true; // 连接后开始等待 m_shouldWait = true; // 连接后开始等待
// 连接前取消之前的重连定时器
m_reconnectTimer.cancel();
//用shared_ptr保持对象存活 //用shared_ptr保持对象存活
auto self = shared_from_this(); auto self = shared_from_this();
@@ -38,7 +41,12 @@ void Client::connect(const std::string& host, int port, bool iAmFirst) {
onConnected(iAmFirst); onConnected(iAmFirst);
} else { } else {
// 处理连接错误 // 处理连接错误
// 包括目标主机拒绝连接等等
std::cerr << "connect failed: " << ec.message() << std::endl; std::cerr << "connect failed: " << ec.message() << std::endl;
if (!m_isGameStart) {
// 尝试重连,理论来说应该是提示一个窗口,要不要重连,这里暂时不处理
attemptReconnect();
}
} }
}); });
} else { } else {
@@ -121,7 +129,7 @@ void Client::waitForOpponent() {
// 检查消息类型 // 检查消息类型
if (netData.type == NetDataType::GAME_START) { if (netData.type == NetDataType::GAME_START) {
std::cout << "Game started! First player is: " << netData.firstPlayer << std::endl; std::cout << "Game started! First player is: " << netData.firstPlayer << std::endl;
m_isGameStart = true;
// 判断自己是否是先手 // 判断自己是否是先手
bool iAmFirst = (netData.firstPlayer == 1 && m_isHost) || bool iAmFirst = (netData.firstPlayer == 1 && m_isHost) ||
(netData.firstPlayer == 2 && !m_isHost); (netData.firstPlayer == 2 && !m_isHost);
@@ -177,10 +185,15 @@ void Client::waitForOpponent() {
} else { } else {
// 关键修复:区分不同类型的错误 // 关键修复:区分不同类型的错误
if (ec == asio::error::eof) { if (ec == asio::error::eof) {
// 对端正常关闭连接 // 如果游戏未开始就尝试重连
std::cout << "Connection closed by peer." << std::endl; if (!m_isGameStart) {
// 不要重试关闭socket attemptReconnect();
closeConnection(); } else {
// 对端正常关闭连接
std::cout << "Connection closed by peer." << std::endl;
// 关闭socket
closeConnection();
}
} else if (ec == asio::error::operation_aborted) { } else if (ec == asio::error::operation_aborted) {
// 操作被取消(正常情况) // 操作被取消(正常情况)
std::cout << "Read operation cancelled." << std::endl; std::cout << "Read operation cancelled." << std::endl;
@@ -210,16 +223,33 @@ void Client::closeConnection() {
} }
m_isWaiting = false; m_isWaiting = false;
m_shouldWait = false; m_shouldWait = false;
// 取消重连定时器
m_reconnectTimer.cancel();
std::cout << "Connection closed." << std::endl; std::cout << "Connection closed." << std::endl;
} }
void Client::attemptReconnect() { void Client::attemptReconnect() {
std::cout << "Attempting to reconnect to " << m_host << ":" << m_port << "...\n"; std::cout << "Attempting to reconnect to " << m_host << ":" << m_port << "...\n";
closeConnection(); closeConnection();
// 等待一段时间后重新连接 // 取消可能存在的之前的定时器
auto self = shared_from_this(); m_reconnectTimer.cancel();
asio::steady_timer timer(m_ioContext, std::chrono::seconds(3));
timer.async_wait([this, self](const asio::error_code& /*ec*/) { // 设置新的定时器
connect(m_host, m_port, m_isHost); m_reconnectTimer.expires_after(std::chrono::seconds(3));
});
auto self = shared_from_this();
m_reconnectTimer.async_wait([this, self](const asio::error_code& ec) {
// 正确检查错误码
if (!ec) {
// 定时器正常触发
std::cout << "Reconnecting after 3 seconds...\n";
connect(m_host, m_port, m_isHost);
} else if (ec == asio::error::operation_aborted) {
// 定时器被取消(可能是我们主动取消的)
std::cout << "Reconnect timer cancelled\n";
} else {
// 其他错误
std::cout << "Reconnect timer error: " << ec.message() << "\n";
}
});
} }

View File

@@ -38,12 +38,14 @@ private:
TurnCallback m_onMyTurn; TurnCallback m_onMyTurn;
TurnCallback m_onGameStart; TurnCallback m_onGameStart;
char m_readBuffer[NetData::size()]; char m_readBuffer[NetData::size()];
asio::steady_timer m_reconnectTimer; // 成员变量
// 新增状态控制 // 新增状态控制
bool m_shouldWait = false; // 是否应该等待对手 bool m_shouldWait = false; // 是否应该等待对手
bool m_isWaiting = false; // 当前是否正在等待 bool m_isWaiting = false; // 当前是否正在等待
bool m_isMyTurn = false; // 是否是我的回合 bool m_isMyTurn = false; // 是否是我的回合
bool m_isGameStart = false; // 检测游戏是否开始
void onConnected(bool iAmFirst); void onConnected(bool iAmFirst);
void waitForOpponent(); void waitForOpponent();

View File

@@ -215,5 +215,7 @@ void SceneManager::destoryQuitedScene() {
const auto info = typeid(*m_quitedScene.get()).name(); const auto info = typeid(*m_quitedScene.get()).name();
m_quitedScene.reset(); m_quitedScene.reset();
std::cout << "SceneManager: " << info << " destroyed\n"; std::cout << "SceneManager: " << info << " destroyed\n";
/*
对于onlinegamescene在点击quit之后因为对象没销毁把点击操作传到了对方一起退出了
*/
} }