Skip to content

怎么注册 udpSocket.bind 这样的函数 #146

@qq547176052

Description

@qq547176052

你的需求是否与某个问题相关? (Is your feature request related to a problem?)

package com.didi.dimina.api.network

import android.util.Log
import com.didi.dimina.api.APIResult
import com.didi.dimina.api.ApiHandler
import com.didi.dimina.api.ApiRegistry
import com.didi.dimina.api.NoneResult
import com.didi.dimina.api.SyncResult
import com.didi.dimina.common.ApiUtils
import com.didi.dimina.engine.qjs.JSValue
import com.didi.dimina.ui.container.DiminaActivity
import org.json.JSONObject
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.InetSocketAddress
import java.nio.charset.StandardCharsets
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger

/**
 * UDP Socket API实现
 * 实现 createUDPSocket, UDPSocket.bind, UDP_test 三个函数
 */
class UdpSocketApi : ApiHandler {

    companion object {
        private const val TAG = "UdpSocketApi"

        // API方法名常量定义
        const val CREATE_UDP_SOCKET = "createUDPSocket"
        const val UDPSOCKET_BIND = "UDPSocket.bind"
        const val UDP_TEST = "UDP_test"
    }

    // 使用线程安全的Map管理UDP Socket实例
    private val udpSockets = ConcurrentHashMap<String, UdpSocketInstance>()
    private val idCounter = AtomicInteger(1)

    fun registerWith(registry: ApiRegistry) {
        // 注册三个API
        registry.register(CREATE_UDP_SOCKET, this)
        registry.register(UDPSOCKET_BIND, this)
        registry.register(UDP_TEST, this)
        Log.d(TAG, "✅ UDP Socket API注册完成: createUDPSocket, UDPSocket.bind, UDP_test")
    }

    override fun handleAction(
        activity: DiminaActivity,
        appId: String,
        apiName: String,
        params: JSONObject,
        responseCallback: (String) -> Unit
    ): APIResult {
        Log.d(TAG, "处理UDP API: $apiName")

        return when (apiName) {
            CREATE_UDP_SOCKET -> createUdpSocket(activity, appId, params, responseCallback)
            UDPSOCKET_BIND -> bindUdpSocket(params, responseCallback)
            UDP_TEST -> udpTest(activity, appId, params, responseCallback)
            else -> {
                Log.w(TAG, "未知的UDP API: $apiName")
                NoneResult()
            }
        }
    }

    /**
     * 1. createUDPSocket - 创建UDP Socket实例
     * 同步API:返回包含UDPSocket对象的SyncResult
     */
    private fun createUdpSocket(
        activity: DiminaActivity,
        appId: String,
        params: JSONObject,
        responseCallback: (String) -> Unit
    ): APIResult {
        Log.d(TAG, "=== createUDPSocket 开始 ===")
        Log.d(TAG, "参数: ${params.toString(2)}")

        return try {
            val socketId = "udp_${System.currentTimeMillis()}"
            val instanceId = idCounter.getAndIncrement()

            Log.d(TAG, "创建Socket实例: $socketId, instanceId: $instanceId")

            // 创建UDP Socket实例
            val udpInstance = UdpSocketInstance(socketId, instanceId)
            udpSockets[socketId] = udpInstance

            // 构建符合微信小程序规范的UDPSocket对象
            // 关键:使用框架期望的方法声明方式
            val udpSocketObj = JSONObject().apply {
                // 基础标识字段
                put("_id", instanceId)
                put("socketId", socketId)

                // 方法声明 - 使用布尔值true,框架会自动创建函数
                put("bind", true)      // 绑定端口方法
                put("send", true)      // 发送消息方法
                put("close", true)     // 关闭Socket方法
                put("onMessage", true) // 监听消息方法
                put("offMessage", true) // 取消监听消息方法

                // 添加类型标识,帮助框架识别对象类型
                put("__className", "UDPSocket")
                put("__type", "native_instance")

                // 成功状态
                put("errMsg", "$CREATE_UDP_SOCKET:ok")
            }

            Log.d(TAG, "✅ UDP Socket创建成功")
            Log.d(TAG, "返回对象: ${udpSocketObj.toString(2)}")

            // 同步返回UDPSocket对象
            val jsValue = createJSValue(udpSocketObj)
            SyncResult(jsValue)

        } catch (e: Exception) {
            Log.e(TAG, "❌ 创建UDP Socket失败", e)
            val errorObj = JSONObject().apply {
                put("errMsg", "$CREATE_UDP_SOCKET:fail")
                put("error", e.message ?: "Unknown error")
            }
            val jsValue = createJSValue(errorObj)
            SyncResult(jsValue)
        } finally {
            Log.d(TAG, "=== createUDPSocket 结束 ===")
        }
    }

    /**
     * 2. UDPSocket.bind - 绑定UDP Socket到指定端口
     * 同步API:返回绑定结果的SyncResult
     */
    private fun bindUdpSocket(
        params: JSONObject,
        responseCallback: (String) -> Unit
    ): APIResult {
        Log.d(TAG, "=== UDPSocket.bind 开始 ===")
        Log.d(TAG, "参数: ${params.toString(2)}")

        return try {
            val socketId = params.optString("socketId", "")
            if (socketId.isBlank()) {
                throw IllegalArgumentException("socketId不能为空")
            }

            Log.d(TAG, "查找Socket实例: $socketId")
            val udpInstance = udpSockets[socketId]
            if (udpInstance == null) {
                throw IllegalArgumentException("UDP Socket不存在: $socketId")
            }

            val port = params.optInt("port", 0)
            val address = params.optString("address", "0.0.0.0")

            Log.d(TAG, "绑定到: $address:$port")

            // 执行绑定操作
            val actualPort = udpInstance.bind(address, port)

            // 构建同步返回结果
            val result = JSONObject().apply {
                put("socketId", socketId)
                put("port", actualPort)
                put("address", address)
                put("errMsg", "$UDPSOCKET_BIND:ok")
            }

            Log.d(TAG, "✅ UDP Socket绑定成功: 端口 $actualPort")

            // 同步返回绑定结果
            val jsValue = createJSValue(result)
            SyncResult(jsValue)

        } catch (e: Exception) {
            Log.e(TAG, "❌ 绑定UDP Socket失败", e)
            val errorObj = JSONObject().apply {
                put("errMsg", "$UDPSOCKET_BIND:fail")
                put("error", e.message ?: "Unknown error")
            }
            val jsValue = createJSValue(errorObj)
            SyncResult(jsValue)
        } finally {
            Log.d(TAG, "=== UDPSocket.bind 结束 ===")
        }
    }

    /**
     * 3. UDP_test - 测试函数,用于调试和验证
     * 同步API:返回测试结果的SyncResult
     */
    private fun udpTest(
        activity: DiminaActivity,
        appId: String,
        params: JSONObject,
        responseCallback: (String) -> Unit
    ): APIResult {
        Log.d(TAG, "=== UDP_test 开始 ===")
        Log.d(TAG, "参数: ${params.toString(2)}")

        return try {
            // 收集测试信息
            val testInfo = JSONObject().apply {
                // 基本信息
                put("test", "UDP Socket API 测试")
                put("timestamp", System.currentTimeMillis())
                put("appId", appId)
                put("activity", activity.javaClass.simpleName)

                // 当前状态
                put("udpSocketsCount", udpSockets.size)
                put("managedSockets", JSONObject(udpSockets.keys.associateWith {
                    udpSockets[it]?.getStatus() ?: "unknown"
                }))

                // 参数信息
                put("receivedParams", params)

                // 测试结果
                put("createUDPSocket", "✅ 已注册")
                put("UDPSocket.bind", "✅ 已注册")
                put("UDP_test", "✅ 正在执行")

                // 系统信息
                put("idCounter", idCounter.get())
                put("thread", Thread.currentThread().name)
            }

            Log.d(TAG, "📊 测试信息收集完成")

            // 执行具体的测试用例
            val testResults = executeTestCases()
            testInfo.put("testResults", testResults)

            // 最终结果
            testInfo.put("overall", if (testResults.optBoolean("allPassed", false)) "✅ 通过" else "❌ 失败")
            testInfo.put("errMsg", "$UDP_TEST:ok")

            Log.d(TAG, "✅ UDP测试完成")
            Log.d(TAG, "测试结果: ${testInfo.toString(2)}")

            // 同步返回测试结果
            val jsValue = createJSValue(testInfo)
            SyncResult(jsValue)

        } catch (e: Exception) {
            Log.e(TAG, "❌ UDP测试失败", e)
            val errorObj = JSONObject().apply {
                put("errMsg", "$UDP_TEST:fail")
                put("error", e.message ?: "Unknown error")
                put("timestamp", System.currentTimeMillis())
            }
            val jsValue = createJSValue(errorObj)
            SyncResult(jsValue)
        } finally {
            Log.d(TAG, "=== UDP_test 结束 ===")
        }
    }

    /**
     * 执行具体的测试用例
     */
    private fun executeTestCases(): JSONObject {
        val results = JSONObject()
        var allPassed = true

        // 测试用例1: 创建Socket测试
        try {
            val testSocketId = "test_udp_${System.currentTimeMillis()}"
            val testInstance = UdpSocketInstance(testSocketId, idCounter.getAndIncrement())

            results.put("createSocket", JSONObject().apply {
                put("status", "✅ 通过")
                put("socketId", testSocketId)
                put("instanceId", testInstance.instanceId)
            })
        } catch (e: Exception) {
            allPassed = false
            results.put("createSocket", JSONObject().apply {
                put("status", "❌ 失败")
                put("error", e.message)
            })
        }

        // 测试用例2: 绑定端口测试
        try {
            val testSocket = DatagramSocket(0) // 使用随机端口测试
            val localPort = testSocket.localPort
            testSocket.close()

            results.put("bindPort", JSONObject().apply {
                put("status", "✅ 通过")
                put("testPort", localPort)
                put("reuseAddress", testSocket.reuseAddress)
            })
        } catch (e: Exception) {
            allPassed = false
            results.put("bindPort", JSONObject().apply {
                put("status", "❌ 失败")
                put("error", e.message)
            })
        }

        // 测试用例3: 网络地址解析测试
        try {
            val localhost = InetAddress.getByName("127.0.0.1")
            val anyAddress = InetAddress.getByName("0.0.0.0")

            results.put("network", JSONObject().apply {
                put("status", "✅ 通过")
                put("localhost", localhost.hostAddress)
                put("anyAddress", anyAddress.hostAddress)
                put("localhostReachable", localhost.isReachable(1000))
            })
        } catch (e: Exception) {
            allPassed = false
            results.put("network", JSONObject().apply {
                put("status", "❌ 失败")
                put("error", e.message)
            })
        }

        results.put("allPassed", allPassed)
        results.put("testCount", 3)
        results.put("passedCount", if (allPassed) 3 else 2) // 简化计算

        return results
    }

    /**
     * 创建JSValue的辅助方法
     */
    private fun createJSValue(jsonObject: JSONObject): JSValue {
        return try {
            // 使用Dimina框架的标准方式创建JSValue
            JSValue.createObject(jsonObject.toString())
        } catch (e: Exception) {
            Log.e(TAG, "创建JSValue失败,使用备用方案", e)
            try {
                // 备用方案:创建空对象
                JSValue.createObject("{}")
            } catch (e2: Exception) {
                Log.e(TAG, "备用方案也失败", e2)
                throw RuntimeException("无法创建JSValue")
            }
        }
    }

    /**
     * UDP Socket实例内部类
     */
    private inner class UdpSocketInstance(
        val socketId: String,
        val instanceId: Int
    ) {
        private var socket: DatagramSocket? = null
        private var isBound = AtomicBoolean(false)
        private var localPort: Int = 0
        private var bindAddress: String = ""

        /**
         * 绑定Socket到指定地址和端口
         */
        @Throws(Exception::class)
        fun bind(address: String, port: Int): Int {
            if (isBound.get()) {
                throw IllegalStateException("Socket已经绑定")
            }

            Log.d(TAG, "执行实际Socket绑定: $address:$port")

            try {
                val inetAddress = InetAddress.getByName(address)

                // 创建并绑定Socket
                socket = DatagramSocket(null).apply {
                    reuseAddress = true // 允许地址重用
                    // 绑定到指定地址和端口
                    bind(InetSocketAddress(inetAddress, if (port == 0) 0 else port))
                }

                isBound.set(true)
                localPort = socket?.localPort ?: 0
                bindAddress = address

                Log.d(TAG, "✅ Socket绑定成功: $address:$localPort")
                return localPort

            } catch (e: java.net.BindException) {
                Log.e(TAG, "端口绑定失败: $port,尝试随机端口", e)

                // 端口被占用时使用随机端口
                socket = DatagramSocket().apply {
                    reuseAddress = true
                }
                isBound.set(true)
                localPort = socket?.localPort ?: 0
                bindAddress = "0.0.0.0" // 随机绑定使用任意地址

                Log.d(TAG, "✅ 随机端口绑定成功: $localPort")
                return localPort
            }
        }

        /**
         * 发送UDP消息
         */
        @Throws(Exception::class)
        fun send(message: String, address: String, port: Int): Int {
            if (!isBound.get()) {
                throw IllegalStateException("Socket未绑定")
            }

            val data = message.toByteArray(StandardCharsets.UTF_8)
            val targetAddress = InetAddress.getByName(address)
            val packet = DatagramPacket(data, data.size, targetAddress, port)

            socket?.send(packet)
            Log.d(TAG, "✅ 发送UDP消息: ${data.size}字节 -> $address:$port")
            return data.size
        }

        /**
         * 关闭Socket
         */
        fun close() {
            isBound.set(false)
            socket?.close()
            socket = null
            Log.d(TAG, "✅ Socket关闭: $socketId")
        }

        /**
         * 获取Socket状态
         */
        fun getStatus(): String {
            return if (isBound.get()) {
                "已绑定到 $bindAddress:$localPort"
            } else {
                "未绑定"
            }
        }
    }

    /**
     * 清理资源
     */
    fun cleanup() {
        Log.d(TAG, "清理UDP Socket资源")
        udpSockets.values.forEach { it.close() }
        udpSockets.clear()
    }
}

c

小程序调用 bindResult = udpSocket.bind(Number(port));
得到 [JS] UDP测试: ❌ 端口绑定失败: not a function

udpSocket.bind 注册不成功
这个应该怎么j
j解决

描述你想要的解决方案 (Describe the solution you'd like)

注册 udpSocket.bind 函数,能被小程序调用

描述你考虑过的替代方案 (Describe alternatives you've considered)

No response

平台 (Platform)

  • Android
  • iOS
  • Harmony
  • Web

补充信息 (Additional context)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions