do-while 循环确保至少执行一次连接尝试,天然适配网络重连场景;while 易导致零次尝试或语义混乱,而 do-while 将探测、判定与清理职责分离,条件仅用于退出决策。

do-while 循环的核心价值在于「先执行、后判断」,这恰好匹配网络重连场景的本质需求:哪怕第一次就成功,也必须触发一次连接尝试;若失败,则根据策略决定是否重试。它不保证成功,但能确保逻辑不被跳过——这是 while 或 for 无法天然做到的。
为什么必须用 do-while 而不是 while
while 循环需预先满足条件才进入,比如 while (!connected),但初始时 connected 是 false,看似可行;问题在于,若你把连接动作写在循环体外(如先 try-catch 再进 while),就可能因异常未捕获而直接退出,导致“零次尝试”。更常见的是语义混乱:为绕过 while 的预判,开发者不得不手动设 boolean shouldTry = true,再写 while (shouldTry),既冗余又易错。do-while 则直白表达“先连一次,再看要不要再连”,条件只负责退出决策,不参与初始化。
关键结构:分离探测、判定与清理
一个健壮的重连逻辑,do 块内只做三件事:发起连接、捕获错误、控制重试节奏;while 条件仅保留纯判断,无副作用:
- 在 do 块开头调用
socket.connect()或httpclient.execute(),并用 try-catch 包裹 - catch 中区分错误类型:对
IOException、ConnectException等临时性异常计数并延时;对UnknownHostException等永久性错误应立即跳出 - 每次失败后调用
Thread.sleep(1000)(或更优的指数退避),并在延时期间响应中断 - while 条件只写
!connected && attempt ,不包含任何方法调用或赋值操作
必须处理的两个隐蔽风险
一是信号中断(EINTR):系统调用被信号打断时返回 -1 且 errno == EINTR,这不是失败,而是应重试。若忽略,连接会提前终止。正确做法是在 catch 外加一层 errno 判断,遇到 EINTR 就 continue,不增加 attempt 计数。
二是资源泄漏:每次循环都新建 Socket 或 HttpClient 实例,但失败时未 close,会导致句柄堆积。务必在 try 块外声明引用,在 finally 或 try-with-resources 中释放,或在 catch 末尾显式调用 socket.close()。
带计数与退避的 Java 示例片段
以下代码最多重试 3 次,首次失败后等 1 秒,第二次等 2 秒,第三次等 4 秒(指数退避):
int attempt = 0;
int maxRetries = 3;
boolean connected = false;
Socket socket = null;
do {
try {
socket = new Socket();
socket.connect(new InetSocketAddress("api.example.com", 443), 5000);
connected = true;
} catch (IOException e) {
if (e instanceof java.net.ConnectException ||
e.getCause() instanceof java.net.SocketTimeoutException) {
attempt++;
if (attempt long delay = (long) Math.pow(2, attempt - 1) * 1000;
Thread.sleep(delay);
}
} else {
break; // 永久错误,不重试
}
} finally {
if (!connected && socket != null) {
try { socket.close(); } catch (IOException ignored) {}
}
}
} while (!connected && attempt










