这两天在写一个 Kotlin Compose 项目,有一个服务类愣是写不明白,几个 AI 问一圈也解决不了。
package com.bhznjns.inputshare.utils
import android.net.LocalServerSocket
import android.net.LocalSocket
import android.net.LocalSocketAddress
import android.util.Log
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import java.io.DataInputStream
import java.io.DataOutputStream
import java.net.SocketException
import java.util.concurrent.Executors
private enum class ServerState {
STOPPED,
LISTENING,
CONNECTED
}
private const val SOCKET_NAME = "inputshare"
class LocalSocketServer {
private companion object {
const val TAG = "LocalSocketServer"
}
private val serverScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val state = MutableStateFlow(ServerState.STOPPED)
private val _messageReceivedFlow = MutableSharedFlow<String>()
private var serverJob: Job? = null
private var serverSocket: LocalServerSocket? = null
private val sendChannel: Channel<ByteArray> = Channel(capacity = 64)
val messageReceivedFlow = _messageReceivedFlow.asSharedFlow()
fun start() {
if (serverJob?.isActive == true) {
Log.w(TAG, "Server is already running.")
return
}
serverJob = serverScope.launch {
try {
serverSocket = LocalServerSocket(SOCKET_NAME)
state.value = ServerState.LISTENING
while (isActive) {
try {
Log.d(TAG, "Waiting for client connection.")
val client = serverSocket!!.accept()
handleClient(client)
} catch (_: SocketException) {
Log.d(TAG, "Server socket closed.")
break
} catch (e: Exception) {
Log.e(TAG, "Client handler failed: ${e.message}")
state.value = ServerState.LISTENING
}
}
} catch (e: Exception) {
Log.e(TAG, "Server failed to start or run: ${e.message}")
} finally {
Log.i(TAG, "Server loop finished.")
state.value = ServerState.STOPPED
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun handleClient(client: LocalSocket) {
state.value = ServerState.CONNECTED
val senderJob = serverScope.launch {
val writer = DataOutputStream(client.outputStream)
try {
for (messageBytes in sendChannel) {
writer.writeInt(messageBytes.size)
writer.write(messageBytes)
writer.flush()
}
} catch (e: Exception) {
Log.e(TAG, "Sender coroutine failed: ${e.message}")
}
}
try {
val reader = DataInputStream(client.getInputStream())
while (serverJob!!.isActive && !senderJob.isCompleted) {
val messageLength = reader.readInt()
when {
messageLength == 0 -> Log.d(TAG, "Received keep-alive signal.")
messageLength < 0 -> Log.e(TAG, "Invalid message length: $messageLength")
else -> {
val messageBytes = ByteArray(messageLength)
reader.readFully(messageBytes)
val message = String(messageBytes)
_messageReceivedFlow.emit(message)
}
}
}
} catch (e: Exception) {
Log.e(TAG, "Client reader: ${e.message}")
} finally {
while (!sendChannel.isEmpty) {
sendChannel.tryReceive()
}
client.close()
runCancellable { senderJob.cancelAndJoin() }
Log.d(TAG, "Client disconnected.")
state.value = ServerState.LISTENING
}
}
fun send(message: String): Boolean {
if (state.value != ServerState.CONNECTED) {
Log.w(TAG, "Cannot send message, no client connected.")
return false
}
val messageBytes = message.toByteArray()
return sendChannel.trySend(messageBytes).isSuccess
}
suspend fun stop() {
Log.d(TAG, "Stopping server.")
state.value = ServerState.STOPPED
sendChannel.close()
serverScope.launch {
try {
val tempClient = LocalSocket()
tempClient.connect(LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.ABSTRACT))
tempClient.close()
} catch (_: Exception) { }
}
Log.d(TAG, "Temp client created and closed.")
serverJob?.join()
serverSocket?.close()
serverScope.cancel()
Log.d(TAG, "Server stopped.")
}
}
遇到的问题是服务在 stop 时无法正确释放 socket,导致重新创建服务时会报 Address already in use 错误
调了一天了也没解决,这 kotlin 太难搞了
![]()