reconnect: не уходить в «ошибку авторизации» при коротком обрыве

После короткого обрыва сервер ещё держит старую сессию и на рукопожатии
присылает MSG_ERROR. Клиент трактовал любую ошибку до STATE_READY как
фатальную (stopFatal → STATE_AUTH_FAILED) и прекращал переподключения.

Теперь ошибки рукопожатия (MSG_ERROR, протокольные сбои, слишком большой
кадр) считаются временными, если успешная сессия уже была (everReady):
показываем STATE_ERROR и продолжаем автопереподключение. Фатально — только
до первой успешной авторизации (реально неверные креды).
This commit is contained in:
ua1zbe
2026-10-09 13:05:05 +03:00
parent 491dff03db
commit 203e5f6ed5

View File

@@ -85,6 +85,15 @@ class ReflectorClient(
@Volatile
private var fatal = false
/**
* Была ли хотя бы одна успешная сессия за текущее подключение.
* Если да — креды уже проверены, поэтому ошибки на рукопожатии считаем
* временными (сервер мог ещё держать старую сессию после обрыва) и
* продолжаем автопереподключение вместо показа «ошибки авторизации».
*/
@Volatile
private var everReady = false
@Volatile
private var tcpSocket: Socket? = null
private var udpSocket: DatagramSocket? = null
@@ -107,6 +116,7 @@ class ReflectorClient(
this.settings = settings
manualDisconnect.set(false)
fatal = false
everReady = false
if (running.get()) {
// Перезапуск с новыми настройками (например, смена профиля)
running.set(false)
@@ -399,6 +409,7 @@ class ReflectorClient(
serverCodecs = info.codecs
callbacks.onServerInfo(info)
state = STATE_READY
everReady = true
callbacks.onState(STATE_READY)
// Сначала сообщаем о себе (версия ПО для дашборда), затем
// выбираем TG для передачи и подписываемся на прослушивание
@@ -441,9 +452,17 @@ class ReflectorClient(
ReflectorProtocol.MSG_ERROR -> {
val err = MsgParser.errorMessage(msg.fields)
Log.w(TAG, "MSG_ERROR: $err")
Log.w(TAG, "MSG_ERROR: $err (state=$state, everReady=$everReady)")
if (state < STATE_READY) {
if (everReady) {
// Креды проверены ранее: ошибка на рукопожатии почти
// всегда временная (сервер ещё держит старую сессию
// после короткого обрыва) — переподключаемся заново.
callbacks.onState(STATE_ERROR, err.ifEmpty { "Переподключение" })
abortSockets()
} else {
stopFatal(err.ifEmpty { "Access denied" })
}
return false
}
callbacks.onError(err)
@@ -452,7 +471,13 @@ class ReflectorClient(
else -> Unit // неизвестные сообщения игнорируются
}
} catch (e: Exception) {
if (everReady) {
// Сбой после успешного подключения — не фатально, переподключаемся.
callbacks.onState(STATE_ERROR, "Ошибка протокола: ${e.message}")
abortSockets()
} else {
stopFatal("Ошибка протокола: ${e.message}")
}
return false
}
return true
@@ -478,7 +503,12 @@ class ReflectorClient(
}
private fun protocolError(detail: String): Boolean {
if (everReady) {
callbacks.onState(STATE_ERROR, detail)
abortSockets()
} else {
stopFatal(detail)
}
return false
}