Android的 WebSocket问题

移动开发 2026-07-09

供参考,服务器的证书是由某个CA签发的。 今天我在Android中使用StompClient实现WebSocket,但连接会瞬间打开又关闭,并抛出以下错误:

java.io.EOFException
    at okio.RealBufferedSource.require(RealBufferedSource.kt:199)
    at okio.RealBufferedSource.readByte(RealBufferedSource.kt:209)
    at okhttp3.internal.ws.WebSocketReader.readHeader(WebSocketReader.kt:119)
    at okhttp3.internal.ws.WebSocketReader.processNextFrame(WebSocketReader.kt:102)
    at okhttp3.internal.ws.RealWebSocket.loopReader(RealWebSocket.kt:293)
    at okhttp3.internal.ws.RealWebSocket$connect$1.onResponse(RealWebSocket.kt:195)
    at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:920)

这是我第一次在Android上实现WebSocket,因此以下是我用来实现的依赖项和逻辑:

依赖项:

com.github.NaikSoftware:StompProtocolAndroid:1.6.6

逻辑代码:

private fun startWebSocketConnection() {
    lifecycleScope.launch( defaultDispatcher) {
        compositeDisposable.clear()
        // Web Socket //
        val url: String = "https://url.ca/guard-ws"

        val rawClient = OkHttpClient.Builder().build()
        val rawRequest = Request.Builder()
            .url(url)
            .addHeader("Authorization", "Bearer ${tokenInterceptor.token}")
            .build()
        rawClient.newWebSocket(rawRequest, object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                Log.d("MyTag", "✅ onOpen: ${response.code}")
                ws.send("CONNECT\naccept-version:1.1,1.2\nheart-beat:0,0\n\n\u0000")
            }
            override fun onMessage(ws: WebSocket, text: String) {
                Log.d("MyTag", "📨 onMessage: $text")
            }
            override fun onClosing(ws: WebSocket, code: Int, reason: String) {
                Log.d("MyTag", "🔴 onClosing: $code / $reason")
            }
            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                Log.e("MyTag", "❌ onFailure: ${t.message}", t)
            }
        })


        webSocketVM.stompClient = Stomp
            .over(
                Stomp.ConnectionProvider.OKHTTP,
                url,
                null,
                webSocketVM.getOkHttpClient()
            )
            .withServerHeartbeat(30000)
            .withClientHeartbeat(30000)
        val list = listOf(StompHeader("Authorization", "Bearer ${tokenInterceptor.token}"))
        webSocketVM.stompClient?.let {
            it.connect( list)
            val main: Disposable = it.lifecycle()
                .subscribe({ lifecycleEvent ->
                    when (lifecycleEvent.type) {
                        Type.OPENED -> {
                            Log.d("MyTag", "intentando con: $url")
                            Log.d("MyTag", "open")
                        }
                        Type.ERROR -> {
                            Log.d("MyTag", "Error: ${lifecycleEvent.exception}")
                        }
                        Type.CLOSED -> {
                            Log.d("MyTag", "closed")
                        }
                        Type.FAILED_SERVER_HEARTBEAT -> {
                            Log.d("MyTag", "failed server heartbeat")
                        }
                        else -> {}
                    }
                }){ error: Throwable? -> Log.e( "MyTag", "error: ${error?.message} : Connection")}
            compositeDisposable.add( main)
        }
    }
}

以下是为WebSocket提供的OkHttpClient:

fun getOkHttpClient(): OkHttpClient {
    return OkHttpClient.Builder()
        .addNetworkInterceptor(
            HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.HEADERS
            }
        ).build()
}

解决方案

问题出在你的URL方案。把 https:// 改为 wss://

kotlin

val url = "wss://url.ca/guard-ws"

OkHttp的 WebSocket客户端需要一个WebSocket方案(ws://wss://)。传递 https:// 会在进行正确的WebSocket升级之前就立即让服务器关闭连接,这正是你所看到的 EOFException


还有另外几处我注意到的点:

你打开了两个连接。原始的 rawClient.newWebSocket(...)Stomp.over(...) 都连接到同一个端点。我猜原始的那个只是用于调试,但完成后删掉它——这是一个额外的、不必要的连接。

在调用connect之前订阅生命周期:

kotlin

compositeDisposable.add(lifecycle) // subscribe first
client.connect(headers)            // then connect

否则在网络较快的情况下,可能会错过 OPENED 事件。

在HTTP升级阶段的鉴权头。你将令牌作为STOMP头放在 connect(list),覆盖了STOMP CONNECT 帧。但如果你的服务器在HTTP升级握手阶段也会验证令牌,你也需要将其添加到 OkHttpClient 拦截器中。

私有CA证书。你提到服务器有一个CA签发的证书。如果是私有CA,Android默认不会信任它,连接会被放弃。你需要一个指向你CA证书的 network_security_config.xml,否则即使修正了方案,也会遇到SSL错误。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章