我正在用Kotlin编写的服务器和Android客户端编写聊天应用程序。我创建了一个后台服务,该服务不断从连接到服务器的套接字读取数据,并在消息到达时发送通知。一切正常,直到用户点击“ x”按钮并关闭应用程序。在执行下面发布的清理代码期间,与服务器的连接失败。在服务设法发送EXIT请求并关闭流之前,服务器已达到EOF。然后,将重新创建服务,但是当它尝试连接到服务器时,它将获得ConnectException(连接被拒绝)。仅在打开省电模式时才会发生。当它关闭或手机通过USB连接到我的笔记本电脑并充电时,没有问题。
ss命令列出有人在指定端口上进行监听,所以这不是问题。我试图以循环方式连接,我。 e。尝试每10秒连接5次,但每次都被拒绝。我尝试在两个不同的端口上侦听,但是即使之前未使用其中一个,两个端口都失败了。医生说默认的积压是50,所以我想也不是。我试图在服务器套接字上设置SO_REUSEADDR标志,但仍然没有执行任何操作。奇怪的是,当我第二次启动应用程序时从应用程序启动服务时,它可以再次连接。因此,我创建了一个广播接收器,以在应用程序崩溃时以与应用程序相同的方式启动该服务,但这对两者均无济于事。
我确实已经使用了一周多的时间来进行谷歌搜索,但这是我同时使用Kotlin和套接字的第一次尝试,但我的想法已经耗尽。如果有人对可能发生的事情有所了解,我将非常感谢您的帮助。
这是onStartCommand服务:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
activeConversation = intent?.getStringExtra(CONV_NAME) ?: ""
login = intent?.getStringExtra(LOGIN) ?: login
if (thread?.isAlive != true) {
thread = thread(start = true) {
synchronized(lock) {
try {
socket = Socket(SERVER_IP, SERVICE_PORT)
output = ObjectOutputStream(socket?.getOutputStream())
input = ObjectInputStream(socket?.getInputStream())
output?.writeObject(Request(START_SERVICE, mutableMapOf(LOGIN to login)))
} catch (e: IOException) {
e.printStackTrace()
return@thread
}
}
handleMessages() //contains input?.readObject() in infinite loop
}
}
return START_STICKY
}
在onDestory()和onTaskRemoved()中,我将此函数称为:
private fun cleanUp() {
synchronized(lock) {
thread(start = true) {
try {
output?.writeObject(Request(EXIT, mutableMapOf(LOGIN to login)))
output?.close()
input?.close()
socket?.close()
nullStreams()
thread?.join()
println("SERVICE: thread joined")
} catch(e: IOException) {
e.printStackTrace()
return@thread
} finally {
println("Service sends broadcast to ask for recreation")
val restartIntent = Intent(this, ServiceRestarter::class.java)
restartIntent.putExtra(LOGIN, login)
sendBroadcast(restartIntent)
}
}.join()
}
}
ServiceRestarter:
class ServiceRestarter : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val login = intent?.getStringExtra(LOGIN)
println("SERVICE RESTARTER: receiving restart request from $login")
val serviceIntent = Intent(context, MessengerService::class.java)
serviceIntent.putExtra(LOGIN, login)
context.startService(serviceIntent)
}}
我的服务器中负责监听的部分:
val clientsSocket = ServerSocket(CLIENTS_PORT)
val serviceSocket = ServerSocket(SERVICE_PORT)
serviceSocket.setReuseAddress(true)
println("Server socket ready!")
println("Service socket port: ${serviceSocket.localPort}")
thread(start = true) {
while(true) ClientThread(clientsSocket.accept(), loggedInUsers, pendingRequests).start()
}
thread(start = true) {
while(true) ServiceThread(serviceSocket.accept(), loggedInUsers).start()
}
和ServiceThread:
class ServiceThread(val socket: Socket,
val loggedInUsers: HashMap<String, UserConnection>) : Thread() {
private var login = ""
private val input = ObjectInputStream(socket.getInputStream())
private val output = ObjectOutputStream(socket.getOutputStream())
override fun run() {
var request = input.readObject() as Request
login = request.content[LOGIN] as String
var userConn: UserConnection?
synchronized(loggedInUsers) {
userConn = loggedInUsers[login]
if(request.action == START_SERVICE) {
println("SERVICE THREAD: New socket conn from $login")
userConn?.run {
println("SERVICE THREAD: putting $login output to logged in users")
serviceStream = output
if(pendingMessage != null) {
output.writeObject(Request(SEND,
mutableMapOf(RESULT to SUCCESS, DATA to pendingMessage)))
pendingMessage = null
}
}
}
}
try { request = input.readObject() as Request }
catch(e: IOException) {
println(e.printStackTrace())
cleanUp()
return@run
}
if(request.action == EXIT) {
println("SERVICE THREAD: Service of user $login is terminating")
cleanUp()
}
}
private fun cleanUp() {
synchronized(loggedInUsers) {
output.close()
input.close()
socket.close()
loggedInUsers[login]?.serviceStream = null
}
}}