Android 连接 SQL Server 问题总结

问题概述

在 Android 应用中使用 Microsoft JDBC 驱动和 jTDS 驱动连接 SQL Server 时遇到严重的 SSL/TLS 兼容性问题。

测试环境

1. 数据库服务器

  • 阿里云 RDS SQL Server
  • 地址:rm-uf633ttx3y74o23iczo.sqlserver.rds.aliyuncs.com:1433
  • 数据库:pda260115
  • 用户:chen / chen@123

2. 测试结果对比

Java 单元测试 ✅ 成功

=== 测试直接JDBC连接(禁用SSL)===
尝试配置: 主配置
  服务器: rm-uf633ttx3y74o23iczo.sqlserver.rds.aliyuncs.com:1433
  数据库: pda260115
  用户名: chen
  ✓ 连接成功
  ✓ SQL Server版本: Microsoft SQL Server 2012 (SP4-GDR) (KB4583465) - ...
✓ JDBC驱动加载成功
测试配置: rm-uf633ttx3y74o23iczo.sqlserver.rds.aliyuncs.com:1433/pda260115
用户名: chen
连接URL: jdbc:sqlserver://rm-uf633ttx3y74o23iczo.sqlserver.rds.aliyuncs.com:1433;databaseName=pda260115;encrypt=false;trustServerCertificate=false;loginTimeout=10
✓ 数据库连接成功
✓ 简单查询测试通过
✓ 连接已关闭

Android 应用运行时 ❌ 失败

2026-01-15 14:47:44.970 JG-DbHelper D Trying 策略1: 简单连接: jdbc:sqlserver://rm-uf633ttx3y74o23iczo.sqlserver.rds.aliyuncs.com:1433;databaseName=pda260115;encrypt=false;trustServerCertificate=false;loginTimeout=5
2026-01-15 14:47:45.109 AndroidRuntime E FATAL EXCEPTION: DefaultDispatcher-worker-1
java.lang.AssertionError: numMsgsRcvd:1 should be less than numMsgsSent:1

问题分析

1. Microsoft JDBC 驱动问题

尝试的驱动版本

  1. 13.2.0.jre8 - 初始版本,SSL握手失败
  2. 12.6.1.jre8 - 降级版本,同样失败
  3. 9.4.1.jre8 - 更低版本,仍然失败

尝试的连接策略

// 策略1: 完全禁用SSL
"encrypt=false;trustServerCertificate=false"

// 策略2: 禁用SSL但信任证书
"encrypt=false;trustServerCertificate=true"

// 策略3: 启用SSL使用TLSv1.2
"encrypt=true;trustServerCertificate=true;sslProtocol=TLSv1.2"

// 策略4: 调整包大小
"encrypt=false;trustServerCertificate=true;packetSize=4096"

系统属性尝试

System.setProperty("com.microsoft.sqlserver.jdbc.disableSSL", "true")
System.setProperty("jsse.enableSNIExtension", "false")
System.setProperty("jdk.tls.client.protocols", "TLSv1.2")
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true")

2. jTDS 驱动问题

配置尝试

// jTDS连接字符串
val url = "jdbc:jtds:sqlserver://${config.server}:${config.port}/${config.database}"

// jTDS参数
props.setProperty("ssl", "off")  // 完全禁用SSL
props.setProperty("useNTLMv2", "false")
props.setProperty("prepareSQL", "0")

问题

  • 驱动加载成功,但连接时仍然出现SSL相关问题
  • 在某些Android版本上可能完全无法工作

根本原因

1. Android SSL/TLS 实现差异

  • Java/JDK环境:使用标准的Java SSL/TLS实现
  • Android环境:使用Conscrypt SSL实现(基于BoringSSL)
  • 不兼容性:Microsoft JDBC驱动的TDS协议实现与Conscrypt不兼容

2. 关键发现

即使设置了 encrypt=false,Microsoft JDBC驱动仍然:

  1. 尝试进行SSL/TLS握手
  2. 发送TDS协议数据包
  3. Android的Conscrypt SSL实现在处理这些数据包时失败

3. 错误信息分析

java.lang.AssertionError: numMsgsRcvd:1 should be less than numMsgsSent:1
    at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(IOBuffer.java:6959)
    at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1872)

这个错误发生在:

  • TDSChannel.enableSSL() 方法中
  • SSL握手过程中
  • 消息计数验证失败

已尝试的解决方案

1. 参数调整方案

// 简化参数(Java测试成功)
val url = "jdbc:sqlserver://${server}:${port};databaseName=${database};encrypt=false;trustServerCertificate=false;loginTimeout=10"
val props = Properties()
props.setProperty("user", username)
props.setProperty("password", password)
props.setProperty("loginTimeout", "10")
props.setProperty("socketTimeout", "30")
// 移除所有SSL相关参数

2. 多策略尝试方案

private fun createConnection(config: DbConfig): Connection {
    return try {
        createConnectionSimple(config)  // 简单连接
    } catch (e: SQLException) {
        try {
            createConnectionNoSSL(config)  // Android特定策略
        } catch (e2: SQLException) {
            testSocketConnection(config)  // 网络诊断
            throw SQLException("JDBC连接失败但网络可达,可能是Android SSL兼容性问题")
        }
    }
}

3. 系统级配置方案

private fun setupAndroidSSLConfig() {
    System.setProperty("com.microsoft.sqlserver.jdbc.disableSSL", "true")
    System.setProperty("jsse.enableSNIExtension", "false")
    System.setProperty("jdk.tls.client.protocols", "TLSv1.2,TLSv1.1,TLSv1")
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true")
}

结论

1. 技术限制(已确认)

  • Microsoft JDBC驱动Android Conscrypt SSL 存在根本性不兼容
  • 即使禁用加密,驱动仍然尝试SSL握手
  • 这个问题在多个驱动版本中都存在
  • 这是已知问题:GitHub conscrypt issue #964 明确记录了此问题

2. 官方问题记录

GitHub conscrypt/issues/964

错误链路:
TDSChannel.enableSSL() 
  → ConscryptEngineSocket.doHandshake()
  → ConscryptEngineSocket$SSLInputStream.processDataFromSocket()
  → 失败

错误信息:
com.microsoft.sqlserver.jdbc.SQLServerException: The driver could not establish 
a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. 
Error: "SQL Server returned an incomplete response. The connection has been closed."

这是 TDS 协议实现与 Android Conscrypt SSL 层的根本性冲突,不是配置问题。

3. 替代驱动调研结果

驱动 状态 结论
Microsoft mssql-jdbc 与 Conscrypt 不兼容 ❌ 不可用
jTDS 官方版本 同样有 SSL 问题 ❌ 不可用
jtds-android 项目已 404 ❌ 不存在
其他驱动 无维护中的替代品 ❌ 无选择

4. 业界共识

Stack Overflow 高票回答:

"Never use a database driver across an Internet connection, for any database, for any platform, for any client, anywhere. That goes double for mobile."

移动端直连数据库是反模式,原因:

  • 网络不稳定,JDBC 设计用于 LAN 环境
  • 安全风险:暴露数据库凭证到客户端
  • 无法做细粒度权限控制
  • 难以演进和维护 API

5. 唯一可行方案:HTTP API 代理

Android App → HTTP/HTTPS → API服务器 → SQL Server

优点

  • 使用标准的 HTTP/HTTPS,无 SSL 兼容性问题
  • 安全:数据库凭证保留在服务器端
  • 灵活:可以添加认证、限流、缓存等
  • 可演进:API 版本控制,不影响客户端

推荐技术栈

  • Node.js + Express:最快搭建,适合快速原型
  • Java Spring Boot:团队熟悉 Java 时推荐
  • Python FastAPI:简洁高效,自动生成文档

其他备选方案

方案 适用场景 复杂度
WebSocket 需要实时双向通信 中等
SQLite + 同步 需要离线支持 较高
GraphQL 复杂查询需求 较高

6. 建议架构

graph TD A[Android App] --> B[HTTP/HTTPS API] B --> C[API服务器 Node.js/Java/Python] C --> D[SQL Server] subgraph "安全边界" B C end subgraph "移动端" A end subgraph "服务器端" D end

后续步骤

  1. 立即:搭建 HTTP API 服务器(推荐 Node.js Express,最快 30 分钟可完成)
  2. 短期:实现 Android 端 HTTP 客户端(使用 Retrofit 或 OkHttp)
  3. 中期:添加认证机制(JWT Token)
  4. 长期:考虑微服务架构,分离数据访问层

参考资料

  1. Microsoft JDBC Driver for SQL Server
  2. Android网络安全配置
  3. Conscrypt - Android的TLS实现
  4. SQL Server连接字符串语法
  5. GitHub Conscrypt Issue #964 - SQL Server JDBC Connection Error
  6. Stack Overflow - Why direct database connection is not recommended

最后更新:2026-01-15
问题状态:已确认为 Android 平台限制,无法通过 JDBC 直连解决
最终方案:采用 HTTP API 代理架构(唯一可行方案)


本文链接:Android 连接 SQL Server 问题总结 - https://h89.cn/archives/501.html

版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。

标签: SQL Server, JDBC, sqlserver, microsoft, jTDS

评论已关闭