更新记录

1.0.0(2026-05-11)

双网络控制 UTS 插件,支持 TCP 服务端、HTTP 请求、WebSocket 连接按需绑定到指定网卡(网线/WiFi/蜂窝)。


平台兼容性

uni-app(5.05)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.05)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式
× ×

L-DualNetworkPlugin

双网络控制 UTS 插件,支持 TCP 服务端、HTTP 请求、WebSocket 连接按需绑定到指定网卡(网线/WiFi/蜂窝)。

插件开发使用版本5.05,其他版本是否支持请自测。


导入

import {
  getAllNetworks,
  isEthernetAvailable,
  isCellularAvailable,
  bindProcessToCellularNetwork,
  bindProcessToWiFiNetwork,
  bindProcessToEthernetNetwork,
  restoreProcessNetwork,
  startTcpServer,
  stopTcpServer,
  isTcpServerRunning,
  getBoundNetworkType,
  getClientCount,
  getClientAddresses,
  sendToClient,
  sendToAll,
  sendHexStrToClient,
  sendHexStrToAll,
  setOnClientConnect,
  setOnClientDisconnect,
  setOnClientMessage,
  httpGet,
  httpPost,
  wsConnect,
  wsSend,
  wsClose,
  wsIsConnected,
} from '@/uni_modules/L-DualNetworkPlugin'

networkType 参数

含义
"ethernet" 以太网(网线)
"wifi" WiFi
"cellular" 蜂窝数据(流量卡)

一、网络检测

getAllNetworks()

获取设备当前全部可用网络。

const nets = getAllNetworks()
// {
//   success: true,
//   msg: "共 2 个网络",
//   networks: [
//     { type: "ethernet", typeDesc: "以太网(网线)" },
//     { type: "wifi",      typeDesc: "WiFi" },
//     { type: "cellular",  typeDesc: "蜂窝网络" }
//   ]
// }

isEthernetAvailable()

if (isEthernetAvailable()) { /* 网线可用 */ }

isCellularAvailable()

if (isCellularAvailable()) { /* 蜂窝可用 */ }

二、进程网络强制绑定

以下方法会调用 Android 系统的 ConnectivityManager.bindProcessToNetwork()强制当前进程中所有网络流量走指定网卡

⚠️ 重要警告:使用后将全局锁定进程网络。例如绑定到蜂窝后,TCP Server 将无法接收来自 WiFi/Ethernet 的连接。建议仅在明确需要全进程网络切换的场景使用,常规 HTTP/WS 请求请直接用 httpGet/httpPost/wsConnectnetworkType 参数按连接指定网卡。

bindProcessToCellularNetwork()

const res = bindProcessToCellularNetwork()
// { success: true, msg: "已绑定进程网络到蜂窝数据", networkType: "cellular" }

bindProcessToWiFiNetwork()

const res = bindProcessToWiFiNetwork()
// { success: true, msg: "已绑定进程网络到 WiFi", networkType: "wifi" }

bindProcessToEthernetNetwork()

const res = bindProcessToEthernetNetwork()
// { success: true, msg: "已绑定进程网络到以太网", networkType: "ethernet" }

restoreProcessNetwork()

恢复为系统默认网络:

const res = restoreProcessNetwork()
// { success: true, msg: "已恢复默认网络" }

三、TCP 服务端

启动时自动检测网络,按 网线 > WiFi > 默认 优先级选择,返回绑定 IP。

startTcpServer(port, callback)

startTcpServer(8080, (res) => {
  if (res.success) {
    // res.networkType: "ethernet" | "wifi" | "default"
    // res.ipAddress:  该网络下的 IPv4 地址,如 "192.168.1.100"
    console.log('TCP 服务启动成功,端口:', 8080)
  } else {
    console.log('启动失败:', res.msg)
  }
})

监听客户端事件

setOnClientConnect((res) => {
  // res.clientKey: 客户端 IP,如 "192.168.1.50"
  // res.clientCount: 当前在线客户端数
  console.log('客户端连接:', res.clientKey)
})

setOnClientDisconnect((res) => {
  console.log('客户端断开:', res.clientKey, '剩余:', res.clientCount)
})

setOnClientMessage((res) => {
  console.log('收到来自', res.clientKey, ':', res.data)
  // res.dataBinary: 原始 ByteArray
})

发送数据

// 普通文本
sendToClient('192.168.1.100', 'Hello Client')  // → boolean
sendToAll('广播消息')                             // → boolean

// 十六进制
sendHexStrToClient('192.168.1.100', 'AABBCCDD') // → boolean
sendHexStrToAll('AABBCCDD')                      // → void

查询状态

getClientCount()          // → number  在线客户端数量
getClientAddresses()      // → string[]  客户端 IP 列表
isTcpServerRunning()      // → boolean
getBoundNetworkType()     // → "ethernet" | "wifi" | "default"

stopTcpServer(callback)

stopTcpServer((res) => {
  console.log(res.success ? '已关闭' : '关闭失败:', res.msg)
})

App 销毁时自动停止服务端,无需手动处理。


四、HTTP(异步回调)

底层使用 Android HttpURLConnection,通过 Network.openConnection() 绑定到指定网卡。

httpGet(networkType, url, callback, params?, headers?)

// 最简调用
httpGet('cellular', 'https://api.example.com/status', (res) => {
  if (res.error) {
    console.error('请求失败:', res.error)
    return
  }
  console.log('HTTP', res.code, '响应:', res.data)
})

// 带 params(query string 格式)
httpGet('wifi', 'https://api.example.com/list', (res) => {
  console.log(res.data)
}, 'pageNum=1&pageSize=10&keyword=测试')
// → URL: https://api.example.com/list?pageNum=1&pageSize=10&keyword=测试

// 带 headers(格式: "key1:val1|key2:val2")
httpGet('wifi', 'https://api.example.com/data', (res) => {}, 
  null,
  'Authorization:Bearer xxx|Accept:application/json')

返回值 HttpResult

字段 类型 说明
code number HTTP 状态码,-1 表示请求失败
data string 响应体字符串
error string 错误信息,成功时为空

httpPost(networkType, url, body, contentType, callback, headers?)

httpPost('cellular', 'https://api.example.com/submit',
  JSON.stringify({ key: 'value' }),   // 请求体
  'application/json',                  // Content-Type
  (res) => {
    console.log('状态码:', res.code, '响应:', res.data)
  },
  'Authorization:Bearer xxx'           // headers(可选)
)

headers 格式

多个 header 用 | 分隔,每个 header 格式为 key:value

"Authorization:Bearer eyJhbGciOiJ|Content-Type:application/json|X-Custom:haha"

五、WebSocket(异步回调,内置心跳检测)

底层自实现 WebSocket 协议,通过 Network.socketFactory 创建 Socket 绑定到指定网卡。

wsConnect(networkType, url, onOpen, onMessage, onClose, onError)

wsConnect(
  'wifi',                                // 网卡类型
  'wss://ws.example.com/socket',        // URL(支持 ws:// 和 wss://)
  () => {
    console.log('WebSocket 已连接')
    wsSend(JSON.stringify({ type: 'hello' }))
  },
  (data) => {
    console.log('收到消息:', data)
    const msg = JSON.parse(data)         // 消息为 JSON 字符串
  },
  () => {
    console.log('WebSocket 已断开')
  },
  (error) => {
    console.error('WebSocket 错误:', error)
  }
)

发送/关闭/状态

wsSend(JSON.stringify({ type: 'ping' }))
wsSend('纯文本消息')
wsClose()
wsIsConnected()  // → boolean

当前仅支持一个 WS 连接。调用 wsConnect 会自动关闭旧连接。

心跳检测(内置,无需配置)

参数 默认值 说明
Ping 间隔 30 秒 客户端定期发送 Ping 帧
Pong 超时 10 秒 发 Ping 后等待 Pong 回执
断线判定 40 秒 累计无响应 → 自动断开 + onError("连接超时,服务器无响应")

服务端发来的 Ping 自动回复 Pong,不影响上层业务。


六、http请求封装示例

提供了 Promise 风格的 HTTP 封装,集成 baseURL + token 注入 + 响应拦截:

import globalConfig from '@/common/config/config.js'
import store from '@/store/index.js'
import dialog from '@/common/utils/dialog.js'
import router from '@/common/utils/router.js'
// #ifdef APP-PLUS
import {
    httpGet,
    httpPost
} from '@/uni_modules/L-DualNetworkPlugin'
// #endif

function buildHeaders(customHeaders) {
    const parts = []
    const token = store.state.token
    if (token) {
        parts.push('Authorization:' + token)
    }
    if (customHeaders) {
        Object.entries(customHeaders).forEach(([k, v]) => {
            parts.push(k + ':' + v)
        })
    }
    return parts.length > 0 ? parts.join('|') : null
}

function handleResponse(res, resolve, reject, options) {

    if (res.error) {
        dialog.toast(res.error)
        reject({
            code: -1,
            msg: res.error
        })
        return
    }

    let data
    try {
        data = res.data ? JSON.parse(res.data) : {}
    } catch (e) {
        dialog.toast('数据解析失败')
        reject({
            code: -1,
            msg: '数据解析失败'
        })
        return
    }

    if (data.code === 200 || data.success) {
        if (data.msg && options?.toast) {
            dialog.toast(data.msg)
        }
        resolve(data)
    } else if (data.code === 401) {
        store.commit('$CGStore', {
            name: 'token',
            value: ''
        })
        store.commit('$CGStore', {
            name: '$userInfo',
            value: ''
        })
        dialog.confirm({
            content: '登录已过期,请重新登录',
            cancelText: '取消',
            confirmText: '去登录'
        }).then(() => {
            router.replace('/pages/login/index')
        })
        reject(data)
    } else {
        if (options?.toast !== false) {
            dialog.toast(data.msg || '请求失败')
        }
        if (options?.catch !== false) {
            reject(data)
        }
    }
}

function request(config) {
    const {
        url,
        method = 'GET',
        data,
        params,
        headers,
        networkType = globalConfig.networkType, // 网卡类型(wifi/cellular/ethernet)
        baseURL = globalConfig.baseURL,
        ...options
    } = config

    const fullUrl = baseURL + url
    const headerStr = buildHeaders(headers)

    return new Promise((resolve, reject) => {
        if (method === 'GET') {
            let queryStr = null
            if (params) {
                queryStr = Object.entries(params)
                    .map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(String(v)))
                    .join('&')
            }
            httpGet(networkType, fullUrl, (res) => {
                handleResponse(res, resolve, reject, options)
            }, queryStr, headerStr)
        } else {
            const body = data ? JSON.stringify(data) : '{}'
            httpPost(networkType, fullUrl, body, 'application/json', (res) => {
                handleResponse(res, resolve, reject, options)
            }, headerStr)
        }
    })
}

const http = {
    get(url, config) {
        return request({
            ...config,
            url,
            method: 'GET'
        })
    },
    post(url, data, config) {
        return request({
            ...config,
            url,
            method: 'POST',
            data
        })
    }
}
export default http
// ==================== 业务接口 ====================

export function getList(data) {
    return http.get('/api/list', {
        params: data
    })
}
export function add(data) {
    return http.post('/api/add', data)
}
import http, { getList } from '@/api/api.js'

// Promise 风格
getList({ cur: '1', date: '2026-05-11' })
  .then(res => console.log('成功:', res))
  .catch(err => console.error('失败:', err))

// 指定网卡
http.get('/api/list', { params: { date: '2026-05-11' }, networkType: 'cellular' })
http.post('/api/add', data, { networkType: 'cellular' })

七、API 速查表

API 签名 说明
getAllNetworks () → AllNetworksResult 获取全部可用网络
isEthernetAvailable () → boolean 网线是否可用
isCellularAvailable () → boolean 蜂窝是否可用
bindProcessToCellularNetwork () → DualNetworkResult 进程绑定到蜂窝(全局)
bindProcessToWiFiNetwork () → DualNetworkResult 进程绑定到 WiFi(全局)
bindProcessToEthernetNetwork () → DualNetworkResult 进程绑定到以太网(全局)
restoreProcessNetwork () → DualNetworkResult 恢复进程默认网络
startTcpServer (port, callback) 启动 TCP 服务端
stopTcpServer (callback) 关闭 TCP 服务端
isTcpServerRunning () → boolean 服务是否运行中
getBoundNetworkType () → string 绑定网络类型
getClientCount () → number 在线客户端数
getClientAddresses () → string[] 客户端 IP 列表
sendToClient (key, msg) → boolean 发文本给客户端
sendToAll (msg) → boolean 广播文本
sendHexStrToClient (key, hex) → boolean 发十六进制给客户端
sendHexStrToAll (hex) → void 广播十六进制
setOnClientConnect (cb) 客户端连接回调
setOnClientDisconnect (cb) 客户端断开回调
setOnClientMessage (cb) 客户端消息回调
httpGet (net, url, cb, params?, headers?) HTTP GET
httpPost (net, url, body, ct, cb, headers?) HTTP POST
wsConnect (net, url, onOpen, onMsg, onClose, onErr) WebSocket 连接
wsSend (msg) WS 发送消息
wsClose () WS 关闭连接
wsIsConnected () → boolean WS 连接状态

八、类型定义

type NetworkInfo = { type: string, typeDesc: string }

type AllNetworksResult = {
  success: boolean, msg: string, networks: NetworkInfo[]
}

type DualNetworkResult = {
  success: boolean, msg: string, networkType?: string
}

type TcpServerResult = {
  success: boolean, msg: string, networkType?: string, ipAddress?: string
}

type ClientConnectResult = {
  clientKey: string, clientCount: number
}

type ClientMessageResult = {
  clientKey: string, data: string, dataBinary?: ByteArray
}

type HttpResult = {
  code: number, data: string, error: string
}

九、注意事项

  1. 需要自定义基座调试:插件包含 Kotlin 原生代码,修改 .uts / .kt 文件后需重新打包自定义基座
  2. Android 6.0+ (API 23):bindProcessToNetwork 需要 API 23,config.json 已设置 minSdkVersion: 23
  3. 进程网络绑定会全局生效:调用 bindProcessToCellularNetwork / bindProcessToWiFiNetwork / bindProcessToEthernetNetwork 后,进程中所有网络流量都将走指定网卡,包括 TCP Server。如需恢复默认网络,调用 restoreProcessNetwork()
  4. HTTP 是异步回调:请求在后台线程执行,不影响 UI
  5. WebSocket 仅支持一个连接wsConnect 会自动关闭旧连接
  6. HTTPS 自签名证书:插件内置 TrustAll 策略,生产环境建议替换

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。