This commit is contained in:
Him188 2020-01-31 23:10:31 +08:00
parent 05dfeb7661
commit 6ef78118ad
8 changed files with 59 additions and 79 deletions

View File

@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket
import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.Cancellable
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.QQImpl
import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private lateinit var channel: PlatformSocket
override suspend fun login() {
if (::channel.isInitialized) {
channel.close()
}
channel = PlatformSocket()
channel.connect("113.96.13.208", 8080)
launch(CoroutineName("Incoming Packet Receiver")) { processReceive() }
this.launch(CoroutineName("Incoming Packet Receiver")) { processReceive() }
// bot.logger.info("Trying login")
var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect()
@ -104,27 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
override suspend fun init() {
//start updating friend/group list
bot.logger.info("Start updating friend/group list")
/*
val data = FriendList.GetFriendGroupList(
bot.client,
0,
20,
0,
0
).sendAndExpect<FriendList.GetFriendGroupList.Response>()
println(data.contentToString())
*/
bot.logger.info("开始加载好友信息")
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
close()
}
}
/*
* 开始加载Contact表
* */
var currentFriendCount = 0
var totalFriendCount: Short = 0
var totalFriendCount: Short
while (true) {
val data = FriendList.GetFriendGroupList(
bot.client,
@ -134,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
0
).sendAndExpect<FriendList.GetFriendGroupList.Response>()
totalFriendCount = data.totalFriendCount
bot.qqs.delegate.addAll(
data.friendList.map {
QQImpl(this@QQAndroidBotNetworkHandler.bot, EmptyCoroutineContext, it.friendUin!!).also {
currentFriendCount++
}
}
)
bot.logger.info("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
data.friendList.forEach {
// atomic add
bot.qqs.delegate.addLast(QQImpl(bot, EmptyCoroutineContext, it.friendUin).also {
currentFriendCount++
})
}
bot.logger.verbose("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
if (currentFriendCount >= totalFriendCount) {
break
}
@ -155,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
).sendAndExpect<FriendList.GetTroopList.Response>(100000)
println(data.contentToString())
*/
}
/**
* 单线程处理包的接收, 分割和连接.
*/
@Suppress("PrivatePropertyName")
private val PacketReceiveDispatcher = newCoroutineDispatcher(1)
/**
* 单线程处理包的解析 (协程挂起效率够)
*/
@Suppress("PrivatePropertyName")
private val PacketProcessDispatcher = newCoroutineDispatcher(1)
/**
* 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
*/
@ -185,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private var expectingRemainingLength: Long = 0
/**
* [PacketProcessDispatcher] 调度器中解析包内容.
* 解析包内容.
*
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
*/
fun parsePacketAsync(input: Input): Job {
return this.launch(PacketProcessDispatcher) {
return this.launch {
input.use { parsePacket(it) }
}
}
@ -337,28 +312,32 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val rawInput = try {
channel.read()
} catch (e: ClosedChannelException) {
dispose()
close()
bot.tryReinitializeNetworkHandler(e)
return
} catch (e: ReadPacketInternalException) {
bot.logger.error("Socket channel read failed: ${e.message}")
dispose()
close()
bot.tryReinitializeNetworkHandler(e)
return
} catch (e: CancellationException) {
return
} catch (e: Throwable) {
bot.logger.error("Caught unexpected exceptions", e)
dispose()
close()
bot.tryReinitializeNetworkHandler(e)
return
}
launch(context = PacketReceiveDispatcher + CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) {
processPacket(rawInput)
launch(CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) {
packetReceiveLock.withLock {
processPacket(rawInput)
}
}
}
}
private val packetReceiveLock: Mutex = Mutex()
/**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
*/
@ -381,7 +360,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
channel.send(delegate)
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate)
}
bot.logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
@ -403,11 +384,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
}
override fun dispose(cause: Throwable?) {
override fun close(cause: Throwable?) {
if (::channel.isInitialized) {
channel.close()
}
super.dispose(cause)
super.close(cause)
}
override suspend fun awaitDisconnection() = supervisor.join()

View File

@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode(
@Serializable
internal class FriendInfo(
@SerialId(0) val friendUin: Long? = 0,
@SerialId(0) val friendUin: Long,
@SerialId(1) val groupId: Byte,
@SerialId(2) val faceId: Short,
@SerialId(3) val remark: String = "",

View File

@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket].
* 只有最终的包才会被包装为 [OutgoingPacket].
*/
@UseExperimental(ExperimentalUnsignedTypes::class)
internal class OutgoingPacket constructor(
name: String?,
val commandName: String,

View File

@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource
import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.qqandroid.utils.guidFlag
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.discardExact
@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
val subCommand = readUShort().toInt()
println("subCommand=$subCommand")
// println("subCommand=$subCommand")
val type = readUByte()
println("type=$type")
// println("type=$type")
discardExact(2)
val tlvMap: TlvMap = this.readTLVMap()
tlvMap.printTLVMap()
// tlvMap.printTLVMap()
return when (type.toInt()) {
0 -> onLoginSuccess(tlvMap, bot)
1, 15 -> onErrorMessage(tlvMap)
@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
bot: QQAndroidBot
): LoginPacketResponse.DeviceLockLogin {
bot.client.t104 = tlvMap.getOrFail(0x104)
println("403 " + tlvMap[0x403]?.toUHexString())
// println("403 " + tlvMap[0x403]?.toUHexString())
return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403))
}
@ -392,10 +391,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
@UseExperimental(MiraiDebugAPI::class)
private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success {
val client = bot.client
println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
//println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
tlvMap[0x150]?.let { client.analysisTlv150(it) }
tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
// tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap[0x161]?.let { client.analysisTlv161(it) }
tlvMap[0x119]?.let { t119Data ->
t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply {

View File

@ -92,7 +92,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor
}
override fun dispose(cause: Throwable?) {
super.dispose(cause)
super.close(cause)
this.heartbeatJob?.cancel(CancellationException("handler closed"))
this.heartbeatJob = null

View File

@ -22,7 +22,8 @@ data class BotAccount(
/**
* 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改.
*/
@MiraiInternalAPI
@Retention(AnnotationRetention.SOURCE)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
@Experimental(level = Experimental.Level.WARNING)
internal annotation class RawAccountIdUse
annotation class RawAccountIdUse

View File

@ -92,20 +92,20 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
try {
if (::_network.isInitialized) {
BotOfflineEvent(this).broadcast()
_network.dispose(cause)
_network.close(cause)
}
} catch (e: Exception) {
logger.error("Cannot close network handler", e)
}
_network = createNetworkHandler(this.coroutineContext)
loginLoop@ while (true) {
_network = createNetworkHandler(this.coroutineContext)
try {
_network.login()
break@loginLoop
} catch (e: Exception) {
e.logStacktrace()
_network.dispose(e)
_network.close(e)
}
logger.warning("Login failed. Retrying in 3s...")
delay(3000)
@ -116,7 +116,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
return _network.init()
} catch (e: Exception) {
e.logStacktrace()
_network.dispose(e)
_network.close(e)
}
logger.warning("Init failed. Retrying in 3s...")
delay(3000)
@ -130,12 +130,12 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@UseExperimental(MiraiInternalAPI::class)
override fun dispose(throwable: Throwable?) {
if (throwable == null) {
network.dispose()
network.close()
this.botJob.complete()
groups.delegate.clear()
qqs.delegate.clear()
} else {
network.dispose(throwable)
network.close(throwable)
this.botJob.completeExceptionally(throwable)
groups.delegate.clear()
qqs.delegate.clear()

View File

@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel
* - Key 刷新
* - 所有数据包处理和发送
*
* [BotNetworkHandler.dispose] 时将会 [取消][Job.cancel] 所有此作用域下的协程
* [BotNetworkHandler.close] 时将会 [取消][Job.cancel] 所有此作用域下的协程
*/
@Suppress("PropertyName")
abstract class BotNetworkHandler : CoroutineScope {
@ -64,12 +64,12 @@ abstract class BotNetworkHandler : CoroutineScope {
/**
* 关闭网络接口, 停止所有有关协程和任务
*/
open fun dispose(cause: Throwable? = null) {
open fun close(cause: Throwable? = null) {
if (supervisor.isActive) {
if (cause != null) {
supervisor.cancel(CancellationException("handler closed", cause))
supervisor.cancel(CancellationException("NetworkHandler closed", cause))
} else {
supervisor.cancel()
supervisor.cancel(CancellationException("NetworkHandler closed"))
}
}
}