资讯动态

短线重连代码实战:从指数退避到避免地址已在使用

发布时间:2026/10/6 16:27:15 来源:尧图企业网站定制
写客户端程序的朋友大概率都碰到过这样的场景网络抖动一下连接就断了如果代码里没有重连机制用户只能手动点一次重连。短线重连就是解决这个问题的常用思路——在连接断开后的短时间内自动尝试重新建立连接让服务快速恢复。这篇文章不讲大而全的框架就聊一个靠谱的短线重连代码该怎么写里面有哪些坑以及为什么你在重连时总会遇到地址已在使用。适合做TCP客户端、IM、物联网采集、消息推送的开发者参考也适合刚接触网络编程、想自己写一套重连逻辑的人。1. 短线重连到底解决什么问题1.1 什么是短线重连场景与目标先说清楚短线这个词。这里不是说股票短线那个短线而是指连接断开后的短时间窗口内程序自动恢复连接。在很多实际业务里断线是常态不是异常。比如手机网络从WiFi切到4G中间会有一两秒的网络不可用比如服务器发布重启连接会被服务端主动关闭比如办公楼里的路由器晚上重启第二天早上来一堆TCP连接全都断了。这些场景如果只靠用户手动重连体验极差尤其对后台服务来说根本不可能有人盯着界面去点重连。短线重连的目标也很直接在用户感知不到或者能容忍的延迟内把连接重新拉起来。这里的短是相对的一般是指毫秒到秒级最多十几秒内完成恢复。如果超过这个时间用户可能已经放弃等待了。所以短线重连和普通的断线重连的一个区别是它非常看重第一次恢复的速度通常第一次重连不会做很长时间的退避而是尽快尝试后续如果仍然失败再逐步延长间隔避免给服务端造成压力。适合用短线重连的场景有客户端连接消息推送服务、聊天服务器、行情推送、远程控制指令通道以及微服务之间的长连接通信。这些场景的共同点是不能容忍连接长时间断开但短时间的抖动可以接受。1.2 为什么不能简单地在连接断开后立刻重连很多人第一版重连代码就是这样的catch到异常马上new一个Socket重新connect。看起来没问题但实际跑一段时间就会出各种怪事。第一立刻重连往往会碰到对端还没完全关闭连接的情况。TCP是双通道的你的客户端收到连接断开的通知不代表服务端对应的socket也马上清理干净了。如果服务端还处于半关闭状态你立刻重连可能建连很快但紧接着对端又把这个连接关掉造成反复连上-断开-连上-断开的循环。第二立刻重连对资源的消耗非常大。每次连接都需要分配文件描述符、缓冲区、线程等资源。如果断线是持续性的比如服务端宕机了那你每次重连都会失败每次失败都走一遍异常处理CPU和内存都可能被拖垮。第三如果大量客户端同时断线同时立刻重连这就是典型的重连风暴。你想象一下一个机房停电后恢复几千个设备在同一秒都尝试连服务器服务器瞬间被新建连接的握手包淹没正常用户反而连不上。所以真正靠谱的重连逻辑一定不是简单的while循环而是有节奏、有上限、有退避策略的调度系统。短线重连的短线对应的是快速的第一次尝试但后续的尝试必须有序地拉开距离给出合理的间隔。1.3 短线重连的三种策略固定间隔、线性退避、指数退避先看最常见的三种重连间隔策略。固定间隔是最简单的一种每次重连间隔固定比如每2秒重试一次。优点是代码简单行为可预测缺点是没有区分故障的严重程度如果服务端需要5分钟才能恢复2秒一次的重试会浪费大量资源。线性退避是每次递增固定值比如第1次等1秒第2次等2秒第3次等3秒。这比固定间隔好一点但如果故障时间长后期间隔会线性增长到很大的值导致恢复变慢。指数退避是目前最常用的方案每重试一次间隔乘以一个倍数。比如初始间隔1秒倍数2那么间隔就是1、2、4、8、16、32秒。因为网络故障很多时候是短时的快速重试几次就能恢复如果几次都不行说明故障大概率不是瞬时抖动这时候间隔越来越大既给服务端恢复时间也不会把自己打死。三种策略可以放在一张表里对比策略间隔序列示例优点缺点适用场景固定间隔2s, 2s, 2s实现简单容易预测无法区分故障程度短时故障恢复慢长时故障浪费资源内网环境对端基本稳定偶发单次抖动线性退避1s, 2s, 3s, 4s比固定间隔均衡后期间隔可能过长恢复不及时对间隔有明确要求的业务指数退避1s, 2s, 4s, 8s短时快速恢复长时自动放缓间隔可能快速增长到很大需要设上限公网环境网络不稳定服务端可能重启短线重连一般就是指数退避的变体第一次重连的间隔很短甚至可以同步重试一次然后走指数退避。后面我会给出具体的参数计算。2. 重连器核心代码实现2.1 顶层设计重连器的状态机先想清楚重连器有几个状态这是设计的基础。我推荐用五个状态IDLE初始状态还未建立连接或者手动停止后进入这个状态。CONNECTING正在发起连接。避免在连接过程中又被重入触发新的连接。CONNECTED连接建立成功正常收发数据。RECONNECTING连接断开正在等待退避计时器或者定时任务正在执行重连。STOPPED已被外部显式关闭不再自动重连。用一个状态转换图来看其实不用图文字讲就行。从IDLE调用connect()进入CONNECTING连接成功进入CONNECTED连接失败或连接后断开进入RECONNECTING并开始退避调度在RECONNECTING状态下如果定时任务触发会先回到CONNECTING再尝试连接如果外部调用close()则进入STOPPED所有定时任务取消。为什么状态机重要因为如果少了一个状态你很可能在并发下重复触发重连。比如连接断开的回调还在执行定时器的任务恰好也到期了两个线程同时尝试connect就会出现创建了两个socket的怪问题。状态机加上AtomicBoolean或者volatile状态字段能有效防止这种重入。2.2 基于Java的短线重连器完整实现我直接用Java写一个可复用的重连器示例。这里选择Java是因为它在网络编程里很常见而且热词里那个java tcp客户端重连时报地址已在使用的问题也正好能在示例中演示。思路其实可以平移到C、Python或者Go。下面这个类实现了一个最核心的短线重连逻辑import java.net.Socket; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; public class ReconnectableTcpClient { private final String host; private final int port; // 重连参数 private final long initialDelayMs; // 第一次重连间隔 private final long maxDelayMs; // 最大间隔 private final int maxRetries; // 最大重试次数-1表示无限 private final double backoffMultiplier; // 退避倍数 private Socket socket; private final ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); private final AtomicBoolean running new AtomicBoolean(false); private final AtomicBoolean connecting new AtomicBoolean(false); private volatile int retryCount 0; public ReconnectableTcpClient(String host, int port) { this(host, port, 1000, 60000, 5, 2.0); } public ReconnectableTcpClient(String host, int port, long initialDelayMs, long maxDelayMs, int maxRetries, double backoffMultiplier) { this.host host; this.port port; this.initialDelayMs initialDelayMs; this.maxDelayMs maxDelayMs; this.maxRetries maxRetries; this.backoffMultiplier backoffMultiplier; } public void connect() { if (running.getAndSet(true)) { return; } retryCount 0; // 第一次连接直接同步尝试让恢复最快 try { connectSync(); } catch (Exception e) { scheduleReconnect(); } } private void connectSync() throws Exception { if (!connecting.compareAndSet(false, true)) { return; } try { // 这里省略了清理旧连接的逻辑后面会讲到 Socket newSocket new Socket(); newSocket.connect(new InetSocketAddress(host, port), 3000); this.socket newSocket; retryCount 0; System.out.println(连接成功: host : port); // 启动心跳和读线程省略 } finally { connecting.set(false); } } private void onDisconnected(Exception e) { if (!running.get()) { return; } System.out.println(连接断开: e.getMessage()); closeQuietly(socket); socket null; scheduleReconnect(); } private void scheduleReconnect() { if (!running.get()) { return; } if (maxRetries ! -1 retryCount maxRetries) { System.out.println(达到最大重试次数停止重连); running.set(false); return; } long delay initialDelayMs; // 指数退避计算加上随机抖动公式后面讲 for (int i 0; i retryCount; i) { delay (long) (delay * backoffMultiplier); } delay Math.min(delay, maxDelayMs); // 加一点抖动防止重连风暴 delay (long) (delay * (0.8 ThreadLocalRandom.current().nextDouble() * 0.4)); retryCount; System.out.println(第 retryCount 次重连间隔 delay ms); scheduler.schedule(() - { try { // 连接前要确保不是已在连接状态 connectSync(); // 如果连接成功retryCount会在connectSync里重置 } catch (Exception ex) { onDisconnected(ex); // 递归调用scheduleReconnect } }, delay, TimeUnit.MILLISECONDS); } public void close() { running.set(false); scheduler.shutdownNow(); closeQuietly(socket); } private void closeQuietly(Socket s) { if (s ! null) { try { s.close(); } catch (Exception ignored) {} } } }这段代码把核心流程展示出来了第一次连接失败后立刻进入重连调度每次重连失败重试次数加一然后根据指数退避计算下一次间隔间隔算完后再加一个随机因子让不同客户端的重连节奏错开重试次数到达上限就彻底停止。注意这里的onDisconnected被定时任务调用时内部又调了scheduleReconnect相当于递归调度。这里的一个隐患是如果connectSync抛出的异常是因为网络一直不通那么每次执行完都会重新调度直到次数耗尽。如果你的最大重试次数是-1那这个递归会一直继续需要注意日志和资源。2.3 关键参数怎么定次数、间隔、上限这部分直接给实际项目里能用的参数参考。短线重连的默认参数初始间隔1000ms。为什么是1秒因为如果网络只是几十毫秒的抖动1秒后重连大概率就已经恢复了。如果设为100ms容易在服务端还没完全清理时反复撞上地址已在使用或者半关闭状态。退避倍数2.0。经典指数退避。倍数太小退避效果不明显倍数太大短时间内容易跳过太多时间窗口。最大间隔60秒。超过这个值说明故障已经持续挺久没必要继续激进地重试。最大重试次数5次。很多人喜欢无限重连但实际业务里无限重连会产生一堆僵尸线程和日志。建议设一个上限比如5次然后切换到人工确认或降级模式。算一笔具体的账。假设初始间隔1s倍数2最大间隔60s最大重试5次。那么每一次的间隔是第1次重试前等待1s第2次2s第3次4s第4次8s第5次16s。加上随机抖动实际在0.8到1.2倍之间波动。总的恢复时间窗口大约是12481631秒如果你在第5次还没连上就停止。如果服务端在30秒内能重启完短线重连基本能恢复。这里可以考虑把最大重试调成6次那么第6次间隔是32s已经接近最大间隔60s整体窗口是63秒。一般来说超过60秒的服务端故障不再是短线可以覆盖的范围了建议交给更上层的巡检或者容错逻辑。这里有一个容易忽略的点设置重连次数上限后要有一个明确的放弃回调。实际操作中我会在类里增加一个onRetryExhausted()方法让业务层做处理比如发告警、切备用端口、显示离线提示。如果没有这个回调客户端只是静默停止用户根本不知道服务已经不可用了。3. 短线重连的硬骨头资源管理与线程安全3.1 防止重连时地址已在使用热词里有java tcp客户端重连时报地址已在使用这是短线重连里一个非常经典的坑。先讲原理。TCP连接由四元组决定本地IP、本地端口、远端IP、远端端口。作为一个客户端正常情况下你不需要手动指定本地端口操作系统会自动分配一个空闲的临时端口。但有些人为了让客户端地址固定或者为了过防火墙会给socket绑定一个固定的本地端口。如果这时候连接断开了旧的socket还没有完全释放或者TCP的TIME_WAIT状态还占着这个端口那你立刻重新绑定同一个端口就会抛BindException报错地址已在使用。就算你不手动绑定本地端口也可能遇到另一种情况你在重连时没有close旧的socket。旧socket明明还活着只是半死状态你又new了一个socket这时候如果底层复用了同一端口就可能冲突。正确做法是每次重连前先把旧socket的输入输出流关闭再close socket置空引用。这个动作必须放在finally里确保异常时也会执行。还有一种情况是服务端的问题。服务端主动断开后服务端的socket处于TIME_WAIT状态占用着服务端端口。如果服务端进程重启可能因为端口没释放而bind失败。这时候客户端再怎么重连也没用只能等TIME_WAIT超时。这在Linux下很常见可以设置服务端SO_REUSEADDR来加速。解决地址已在使用的常用手段客户端不要手动绑定固定本地端口让系统分配。重连之前先关闭旧连接并且延迟一点点时间比如100ms让内核完成清理。如果是服务端在listen之前socket.setReuseAddress(true)。检查代码里是否用了Socket的getInputStream()没有关闭InputStream不关Socket也关不掉。下面是在重连器里增加清理逻辑的片段private void closeQuietly(Socket s) { if (s null) return; try { // 先关输入输出流再关socket InputStream in s.getInputStream(); OutputStream out s.getOutputStream(); if (in ! null) in.close(); if (out ! null) out.close(); s.close(); } catch (Exception ignored) { } }实际开发中我遇到过这样一个问题业务线程在读socket的时候被阻塞在read()上断开通知没有及时处理导致重连逻辑一直没跑。这时候你光靠重连器本身是不够的必须给read操作设置超时时间。Socket默认是无限阻塞的所以最好设一个SO_TIMEOUT比如30秒一旦超时触发一次主动断开和重连。3.2 连接对象的管理close、finally、隔离重连器里最忌讳的是资源泄露。每次重连失败如果socket没有正确close文件描述符就会被耗尽最后报Too many open files整个进程就挂掉了。落实到代码上有几点要注意。第一connect失败时socket对象虽然创建了但可能没有完全建立也需要close。很多初学者只在自己new Socket成功之后才关心关闭其实失败时也要关。第二清理旧连接的逻辑要放在新连接创建之前。如果在连接成功后才发现旧的还没关那就已经晚了。第三不要用一个socket实例去重连。如果你把socket的引用直接赋给新对象旧的没关就会泄露。我习惯在每次连接前调用closeQuietly(socket)再置null然后创建新socket。用一个单独的connectSync方法统一管理。第四当连接断开时所有依赖这个连接的数据流、读线程、写队列都要做隔离。比如消息重发队列如果新连接建立后还往旧的缓冲里写数据就丢了。我通常在onDisconnected里清空队列或者把待发消息转到新的队列。这里给一个异常处理模板在connectSync里用try-catch-finally包住整个建连过程private void connectSync() throws Exception { if (!connecting.compareAndSet(false, true)) return; try { closeQuietly(socket); // 清理旧连接 Socket newSocket new Socket(); newSocket.connect(new InetSocketAddress(host, port), 3000); socket newSocket; retryCount 0; } finally { connecting.set(false); } }为什么要在finally里重置connecting状态因为如果不重置一旦连接失败connecting永远为true后续重连永远无法发起。这是实际项目里一个很低级但很常见的bug。3.3 线程安全定时任务与主线程的竞争Java里的ScheduledExecutorService用起来很方便但会产生线程竞争。比如连接断开的回调发生在读线程里同时定时任务的线程也在执行connectSync两个线程可能同时进入connectSync。虽然我用AtomicBoolean的compareAndSet挡住了其中一个但如果connecting状态不是在方法最开始判断还是会有race condition。我的建议是所有对socket引用的读写都集中在同一个线程里或者用锁保护。最简单的方式是让重连器的所有方法都通过同一个单线程的executor执行比如用ScheduledThreadPoolExecutor把connect、onDisconnected、定时任务全部丢到这个单线程池里天然串行。这样就不用手动加锁代码更简洁。看一下具体实现private final ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); public void connect() { scheduler.submit(() - { if (running.getAndSet(true)) return; try { connectSync(); } catch (Exception e) { scheduleReconnect(); } }); } private void onDisconnected(Exception e) { scheduler.submit(() - { closeQuietly(socket); socket null; scheduleReconnect(); }); }如果监听连接的线程和重连器的线程不是同一个那么断开事件可以调用onDisconnected它会把真正的处理逻辑投递到单线程池。这样所有的socket操作都在同一条执行线上不会出现同时读写socket的情况。这里还有一个细节close()方法里调用了scheduler.shutdownNow()这个操作会把还在排队的重试任务全部取消。如果你不希望close之后还有任务在跑这里就可以。但如果你希望close之后还能恢复到IDLE状态继续使用那shutdownNow就不合适了应该用一个独立的关闭标志位让排队任务自行判断后退出。4. 常见问题与排查技巧实录4.1 重连风暴多个客户端同时重连炸掉服务端这是我见过破坏力最大的问题。应用发布时服务端会关闭所有连接客户端收到断开通知后马上重连。如果几千个客户端都这么做服务端的accept队列瞬间被打满CPU飙升新连接握手超时反而更慢。解决手段有两个一是重连间隔加随机抖动二是在服务端做连接准入控制。抖动实现很简单给计算出的delay乘一个随机系数long delay (long) (baseDelay * (0.8 ThreadLocalRandom.current().nextDouble() * 0.4));这个系数让每个客户端的实际间隔分布在基础间隔的80%到120%之间避免整群同步。更彻底的方案是让客户端支持重启保护如果服务端主动断开可以下发一个广播告诉客户端不要立刻重连等几分钟。这个做起来比较复杂但很多消息推送系统都这么干。如果你的服务能感知客户端数量还可以做指数退避外的全量延迟。4.2 心跳超时与短线重连的配合短线重连不能只被动等断开事件。很多时候连接并没有真正断开只是网络半死比如交换机端口老化TCP连接还挂着但数据包已经发不出去了。这时候客户端可能等几十秒才发现问题。为了做到短线就要心跳机制兜底。心跳的核心是客户端定时发送一个ping包服务端回pong。如果客户端在约定时间内没有收到pong就判定连接失效主动close触发重连。注意TCP本身有保活机制SO_KEEPALIVE默认2小时对短线来说太慢了。心跳和重连的间隔要配合。比如你的短线重连第一次间隔是1秒那么心跳超时最好设置在3到5秒以内这样从断网到第一次重连的总耗时控制在几秒内。心跳超时设置太长短线就变成长线了。实际编码时我会在连接成功后启动一个定时任务每隔heartbeatInterval发送一次心跳同时记录最近一次收到pong的时间。每次收到数据包都刷新这个时间。然后另一个定时任务检查这个时间如果超过timeout就主动触发onDisconnected而不必等待底层的socket异常。4.3 日志轰炸与重连时的降级处理重连失败时如果每次都在日志里打印异常堆栈一次断网可能打出几万行日志直接把磁盘灌满。我在生产环境就见过这种事故。我的处理原则是在状态变化时打日志不要在每次尝试重连时都打。比如开始第5次重连打一条连接成功打一条。如果需要记录失败原因可以用debug级别或者只记录异常的类名和message不打印完整堆栈。降级处理是另一个关键点。重连期间新的业务请求怎么办比如消息系统如果客户端连不上服务器又不想丢失用户发的消息应该先把消息缓存到本地队列等连接恢复后重发。这个队列最好有大小限制防止内存爆掉。简单做法private final BlockingQueueString pendingMessages new LinkedBlockingQueue(10000); private void handleSend(String msg) { if (isConnected()) { try { socket.getOutputStream().write(msg.getBytes()); } catch (Exception e) { pendingMessages.offer(msg); } } else { pendingMessages.offer(msg); } }连接成功后先清空pendingMessages再恢复正常发送。注意要控制重发顺序和确认机制避免重复发送。4.4 真实案例Java TCP客户端重连时报地址已在使用的排查思路我们复盘一个真实的排查过程。现象是客户端代码里写了socket.bind(new InetSocketAddress(localPort))每次断线重连过一会儿就报BindException: Address already in use重连永远失败。排查步骤第一步看代码确认bind了固定端口。客户端bind本地端口确实有些场景需要但绝大多数是不需要的。第二步在Linux上跑netstat -an | grep 端口发现旧连接处于TIME_WAIT状态占着这个本地端口。TIME_WAIT需要一段时间才能消失默认60秒但你等不了。第三步尝试在bind之前先close旧socket。改完还是不行因为TIME_WAIT状态在socket close之后依然存在要等2MSL。第四步给bind之前加上setReuseAddress(true)Socket sock new Socket(); sock.setReuseAddress(true); sock.bind(new InetSocketAddress(localPort)); sock.connect(new InetSocketAddress(host, port), 3000);但这只是缓解因为TIME_WAIT仍然占用端口。真正的解决方案是去掉固定端口绑定让系统动态分配这样每个重连的本地端口都是新的就不会撞上TIME_WAIT。还有一个思路服务端断开后客户端不要傻等TIME_WAIT结束而是直接换一个本地端口去连接。很多推流服务就是这么做的。所以最干净的办法就是动态端口。除非你对端侧防火墙只允许特定端口出站否则别绑固定端口。4.5 常见问题速查表把我在实际中遇到的高频问题整理成一个表方便快速定位。症状可能原因解决方案重连时报地址已在使用绑定了固定本地端口且旧连接TIME_WAIT去掉固定端口绑定或开启SO_REUSEADDR连接一直重连但从不成功服务端未启动或网络不通重试次数不够多检查服务端调大maxRetries用telnet测试端口连通性第一次重连成功但立刻又断开旧连接未清理干净对端半关闭状态重连前强制close旧socket延长第一次重连间隔到1s以上内存或者文件描述符耗尽每次重连创建的socket没有close用closeQuietly并在finally中执行用资源监控工具确认fd数量大量客户端同时重连导致服务端无响应重连风暴所有客户端同步重试加随机抖动增加初始间隔服务端做最大连接数限制网络恢复后客户端不发消息了重连成功但未恢复发送线程或队列连接成功回调中重新启动发送任务将pending队列统一处理5. 我的实操经验与几个小技巧5.1 重连参数不要写成死数字做成可配置我早期习惯把重连次数、初始间隔直接写在代码里后来发现每次调参都要发版太痛苦了。现在我会把这些参数放到配置文件里甚至用远程配置中心。因为不同环境的网络状况差异非常大内网可能1秒间隔就够了公网可能要3秒起步。可配置还可以让你在线上出问题时不用改代码直接通过配置把重连次数调大或调小。如果不想引入配置中心至少要放在一个常量类里集中管理并且允许构造函数注入。我上面写的第二个构造方法就是干这个的。5.2 第一次重连同步尝试之后异步退避我的习惯是连接断开后的第一次尝试不等待任何延迟直接同步尝试。因为很多瞬时抖动在你还没来得及调度退避任务的时候就已经恢复了。如果同步尝试失败再进入异步退避。这样恢复速度最快用户体验最好。注意同步尝试时不要卡住唯一的调度线程所以如果采用单线程池第一次尝试也丢到调度线程里跑但不要加delay。要实现这个效果只需要把scheduleReconnect里的delay计算出来但第一个定时任务其实是立即执行的。可以写成0延迟任务也可以单独调用connectSync。5.3 用状态回调代替布尔标志我见过很多人用isConnected()这样的布尔方法判断客户端状态但在重连期间这个状态会频繁变化很容易让业务层误判。更好的做法是提供几个回调onConnected、onDisconnected、onReconnecting、onRetryExhausted。业务层通过这些回调驱动状态而不是自己主动去查询。这样解耦性更强测试也方便。比如界面端可以在onDisconnected后显示连接断开正在重连在onConnected后隐藏。后台任务可以在onReconnecting时暂停外发请求。这个设计虽然多写几个接口但后期收益很大。5.4 测试重连逻辑不能只靠拔网线很多测试同学喜欢直接拔网线来模拟断网但这样只会触发网卡down不一定让TCP连接进入断开状态。更好的方式是用防火墙规则直接丢弃某个端口的数据包或者用tc命令模拟丢包和延迟。Linux下可以这样模拟网络故障sudo iptables -A INPUT -p tcp --dport 8080 -j DROP sudo iptables -A OUTPUT -p tcp --sport 8080 -j DROPDROP不会给对端发RST包TCP连接会一直挂在半死状态这时候你才能测出心跳超时和短线重连是否真的工作。恢复时删除规则sudo iptables -D INPUT -p tcp --dport 8080 -j DROP sudo iptables -D OUTPUT -p tcp --sport 8080 -j DROP如果想模拟服务端重启直接在服务端kill掉进程再重新启动。这时客户端的TCP连接会收到对端的FIN包触发快速断开和真实故障一致。5.5 如果项目允许优先用现成的框架最后说点实在的。如果项目里已经用了Netty、MINA或者gRPC这类通信框架它们多半自带了重连能力或者有社区成熟的扩展。比如Netty里你可以监听channelInactive事件然后触发channelActive的重连逻辑。但框架默认的重连策略往往很简单你必须自己改退避算法。所以自研一个小重连器并不算重复造轮子反而能让你更清楚框架内部在做什么。但如果你只是在写一个简单的工具脚本没必要自己实现直接用一个支持重连的库更好。毕竟重连这件事核心难点不在重连本身而在如何优雅地重连。我写这套短线重连代码最终的体会就是一个原则把重连看作一个独立的子系统带着状态、策略、资源管理去设计而不是在异常catch里草率地new一个Socket。踩过几次坑之后你会发现重连写得好程序的稳定性会有一个质的提升。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑