Android的 WebSocket问题
供参考,服务器的证书是由某个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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。