Browse Source

Fix retry when no Internet connection

Him188 5 years ago
parent
commit
fa211cab5c

+ 3 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt

@@ -39,6 +39,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
 import net.mamoe.mirai.qqandroid.utils.NoRouteToHostException
 import net.mamoe.mirai.qqandroid.utils.PlatformSocket
+import net.mamoe.mirai.qqandroid.utils.SocketException
 import net.mamoe.mirai.qqandroid.utils.io.readPacketExact
 import net.mamoe.mirai.qqandroid.utils.io.useBytes
 import net.mamoe.mirai.qqandroid.utils.retryCatching
@@ -127,9 +128,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
 
         while (isActive) {
             try {
-                channel.connect(host, port)
+                channel.connect(coroutineContext + CoroutineName("Socket"), host, port)
                 break
-            } catch (e: NoRouteToHostException) {
+            } catch (e: SocketException) {
                 logger.warning { "No route to host (Mostly due to no Internet connection). Retrying in 3s..." }
                 delay(3000)
             }

+ 4 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt

@@ -33,10 +33,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
 import net.mamoe.mirai.qqandroid.utils.ByteArrayPool
 import net.mamoe.mirai.qqandroid.utils.NoRouteToHostException
 import net.mamoe.mirai.qqandroid.utils.PlatformSocket
+import net.mamoe.mirai.qqandroid.utils.SocketException
 import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
 import net.mamoe.mirai.qqandroid.utils.io.withUse
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.copyAndClose
+import kotlin.coroutines.EmptyCoroutineContext
 
 @OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
 @Suppress("SpellCheckingInspection")
@@ -117,9 +119,9 @@ internal object HighwayHelper {
         val socket = PlatformSocket()
         while (client.bot.network.isActive) {
             try {
-                socket.connect(serverIp, serverPort)
+                socket.connect(EmptyCoroutineContext, serverIp, serverPort)
                 break
-            } catch (e: NoRouteToHostException) {
+            } catch (e: SocketException) {
                 delay(3000)
             }
         }

+ 3 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/PlatformSocket.kt

@@ -13,13 +13,14 @@ import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.Closeable
 import kotlinx.io.errors.IOException
 import net.mamoe.mirai.utils.Throws
+import kotlin.coroutines.CoroutineContext
 
 /**
  * 多平台适配的 TCP Socket.
  */
 internal expect class PlatformSocket() : Closeable {
-    @Throws(NoRouteToHostException::class)
-    suspend fun connect(serverHost: String, serverPort: Int)
+    @Throws(SocketException::class)
+    suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int)
 
     /**
      * @throws SendPacketInternalException

+ 0 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/input.kt

@@ -27,7 +27,6 @@ import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmSynthetic
 
-@MiraiInternalAPI
 internal inline fun <R> ByteReadPacket.useBytes(
     n: Int = remaining.toInt(),//not that safe but adequate
     block: (data: ByteArray, length: Int) -> R
@@ -36,7 +35,6 @@ internal inline fun <R> ByteReadPacket.useBytes(
     block(it, n)
 }
 
-@MiraiInternalAPI
 internal inline fun ByteReadPacket.readPacketExact(
     n: Int = remaining.toInt()//not that safe but adequate
 ): ByteReadPacket = this.readBytes(n).toReadPacket()

+ 112 - 2
mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/PlatformSocket.kt

@@ -21,6 +21,7 @@ import java.io.BufferedInputStream
 import java.io.BufferedOutputStream
 import java.net.Socket
 import java.net.SocketException
+import kotlin.coroutines.CoroutineContext
 
 /**
  * 多平台适配的 TCP Socket.
@@ -81,7 +82,7 @@ internal actual class PlatformSocket : Closeable {
     }
 
     @OptIn(ExperimentalIoApi::class)
-    actual suspend fun connect(serverHost: String, serverPort: Int) {
+    actual suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int) {
         withContext(Dispatchers.IO) {
             socket = Socket(serverHost, serverPort)
             readChannel = socket.getInputStream().buffered()
@@ -92,4 +93,113 @@ internal actual class PlatformSocket : Closeable {
 
 actual typealias NoRouteToHostException = java.net.NoRouteToHostException
 
-actual typealias SocketException = SocketException
+actual typealias SocketException = SocketException
+
+// ktor aSocket
+
+/*
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package net.mamoe.mirai.qqandroid.utils
+
+import io.ktor.network.selector.ActorSelectorManager
+import io.ktor.network.sockets.*
+import io.ktor.util.KtorExperimentalAPI
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.ByteWriteChannel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
+import kotlinx.io.core.ExperimentalIoApi
+import kotlinx.io.core.buildPacket
+import kotlinx.io.errors.IOException
+import net.mamoe.mirai.qqandroid.utils.io.useBytes
+import java.net.InetSocketAddress
+import java.net.SocketException
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * 多平台适配的 TCP Socket.
+ */
+internal actual class PlatformSocket : Closeable {
+    private lateinit var socket: io.ktor.network.sockets.Socket
+
+    actual val isOpen: Boolean
+        get() =
+            if (::socket.isInitialized)
+                !socket.isClosed
+            else false
+
+    actual override fun close() {
+        if (::socket.isInitialized) {
+            socket.close()
+        }
+    }
+
+    @PublishedApi
+    internal lateinit var writeChannel: ByteWriteChannel
+
+    @PublishedApi
+    internal lateinit var readChannel: ByteReadChannel
+
+    actual suspend fun send(packet: ByteArray, offset: Int, length: Int) {
+        withContext(Dispatchers.IO) {
+            writeChannel.writeFully(packet, offset, length)
+        }
+    }
+
+    /**
+     * @throws SendPacketInternalException
+     */
+    actual suspend fun send(packet: ByteReadPacket) {
+        withContext(Dispatchers.IO) {
+            try {
+                packet.useBytes { data: ByteArray, length: Int ->
+                    writeChannel.writeFully(data, offset = 0, length = length)
+                }
+            } catch (e: IOException) {
+                throw SendPacketInternalException(e)
+            }
+        }
+    }
+
+    /**
+     * @throws ReadPacketInternalException
+     */
+    actual suspend fun read(): ByteReadPacket {
+        return withContext(Dispatchers.IO) {
+            try {
+                buildPacket {
+                    readChannel.read {
+                        writeFully(it)
+                    }
+                }
+            } catch (e: IOException) {
+                throw ReadPacketInternalException(e)
+            }
+        }
+    }
+
+    @OptIn(ExperimentalIoApi::class, KtorExperimentalAPI::class)
+    actual suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int) {
+        withContext(Dispatchers.IO) {
+            socket = aSocket(ActorSelectorManager(coroutineContext)).tcp().tcpNoDelay()
+                .connect(InetSocketAddress(serverHost, serverPort))
+            readChannel = socket.openReadChannel()
+            writeChannel = socket.openWriteChannel(true)
+        }
+    }
+}
+
+actual typealias NoRouteToHostException = java.net.NoRouteToHostException
+
+actual typealias SocketException = SocketException
+ */