上一篇
安卓与服务器长连接
- 行业动态
- 2025-04-24
- 11
安卓客户端与服务器长连接可通过WebSocket实现,需心跳检测维持链路,处理网络切换及断线重
长连接基础概念
长连接指客户端与服务器之间建立的持久化连接,区别于每次请求重新建立的短连接,在安卓应用中,长连接常用于实时通信(如聊天)、状态同步(如定位)、推送服务等场景。
核心特点:
- 持久性:连接建立后长期保持,减少重复握手开销。
- 双向通信:服务器可主动推送消息到客户端。
- 低延迟:无需频繁创建连接,适合实时交互。
协议选择与对比
协议 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
TCP | 自定义协议、文件传输 | 通用性强,支持全双工通信 | 需自行处理心跳、断线重连 |
WebSocket | 浏览器兼容、即时通讯 | 标准协议,支持跨域,低延迟 | 依赖浏览器支持,安卓需兼容低版本 |
MQTT | 物联网、消息推送 | 轻量级,支持主题订阅,省电 | 需部署 MQTT Broker,协议复杂度较高 |
安卓实现长连接的常见方式
基于 TCP
的原生 Socket
// 创建客户端 Socket Socket socket = new Socket("server_ip", port); // 发送数据 OutputStream out = socket.getOutputStream(); out.write("Hello Server".getBytes()); // 接收数据 InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = in.read(buffer); String response = new String(buffer, 0, len);
使用 OkHttp
库(推荐)
OkHttpClient client = new OkHttpClient.Builder() .readTimeout(0, TimeUnit.MILLISECONDS) // 禁用读取超时 .build(); Request request = new Request.Builder() .url("wss://yourserver.com/socket") // WebSocket 地址 .build(); WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { // 连接成功 } @Override public void onMessage(WebSocket webSocket, String text) { // 接收消息 } });
第三方库(如 Socket.IO)
// Android 端使用 Socket.IO 客户端 IO.Options opts = new IO.Options(); opts.secure = true; // 启用 SSL Socket socket = IO.socket("https://yourserver.com", opts); socket.connect(); socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() { @Override public void call(Object... args) { // 连接成功 } });
关键问题与解决方案
心跳检测(Keep-Alive)
- 作用:检测连接存活状态,防止因网络中断或服务器超时导致断连。
- 实现:
- 定期发送自定义心跳包(如每 30 秒)。
- 服务器响应心跳包,客户端根据响应判断连接状态。
// 使用 Handler 定时发送心跳 Handler handler = new Handler(Looper.getMainLooper()); Runnable heartbeat = () -> { if (socket.isConnected()) { socket.getOutputStream().write("HEARTBEAT".getBytes()); } handler.postDelayed(heartbeat, 30_000); // 30秒一次 }; handler.post(heartbeat);
网络切换处理
- 问题:从 Wi-Fi 切换到移动网络时,IP 地址变化可能导致连接中断。
- 解决方案:
- 监听网络状态变化(
NetworkCallback
),断线后自动重连。 - 使用域名而非 IP 地址,依赖 DNS 解析新地址。
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkRequest request = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build(); cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { // 网络恢复,尝试重连 reconnect(); } });
- 监听网络状态变化(
电量优化
- 问题:长连接会持续占用网络资源,可能导致电量消耗过快。
- 优化策略:
- 降低心跳频率(如从 10 秒延长到 30 秒)。
- 在后台时暂停非必要连接(如进入
Doze
模式后断开)。 - 使用
JobScheduler
或WorkManager
调度心跳任务。
断线重连机制
- 策略:
- 指数退避重试(如首次重试间隔 2 秒,最大重试次数 5 次)。
- 重连时携带上次断开的上下文(如未发送的消息队列)。
private void reconnect() { int retryCount = 0; while (retryCount < MAX_RETRY) { try { socket = new Socket("server_ip", port); break; // 重连成功 } catch (IOException e) { retryCount++; try { Thread.sleep((long) Math.pow(2, retryCount) 1000); } catch (InterruptedException ex) {} } } }
安全性保障
- SSL/TLS 加密:防止数据被窃听或改动。
// OkHttp 启用 HTTPS OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(SSLContext.getDefault().getSocketFactory(), trustManager) .build();
- 身份认证:使用 Token(如 JWT)或 API Key 验证客户端合法性。
常见问题与解答
问题1:长连接在安卓后台运行时容易被杀死,如何解决?
解答:
- 将应用添加到电池优化白名单(引导用户手动设置)。
- 使用
Foreground Service
并显示通知,避免被系统回收。 - 在
AndroidManifest.xml
中声明android:killBackgroundProcesses="false"
(需用户授权)。
问题2:如何测试长连接的稳定性?
解答:
- 网络模拟:使用工具(如 Charles、Network Link Conditioner)模拟高延迟、丢包、断网等场景。
- 压力测试:通过脚本模拟大量客户端并发连接,观察服务器和客户端的抗压能力。
- 日志监控:在客户端和服务器端记录关键事件(如连接断开、重连次数、心跳响应时间)。