在用户同时开启 wifi/cellular 时,期望强行使用 cellular 访问网络,这是个比较常规的诉求。 鸿蒙提供了大量的 ets 网络基础库,本来以为能简单实现这个能力,却有些令人不解的坑。 作为基建来说,规避 ets 而直接使用 C++ 可能才是最优解。
获取蜂窝网络 IP
第一个坑就是connection.getAllNets()接口在 wifi/cellular 同时开启时并不会返回 cellular 的NetHandle,仅开启 cellular 才能拿到其NetHandle。
但发现监听NetConnection的netConnectionPropertiesChange事件后,在网络不变化时,仅开启 wifi 会回调、仅开启 cellular 不回调、wifi/cellular 同时开启时会回调两个网卡的NetHandle。
这两个接口诡异的表现,结合起来却恰恰能拿到 cellular 的 NetHandle。
Socket 指定本地 IP
第二个坑是socket.TCPSocket的bind操作是个虚假接口,意味着它并不会使用你传入的 IP 去绑定,难以理解设计者的初衷,经过与鸿蒙侧的沟通,这似乎不是必须解决的问题,也给不出有效解决方案。
看遍 ets 网络文档,发现一些可行性。
socket.TLSSocket是支持bind指定 IP 的,当它却不支持指定网卡(也就是NetHandle),无法构建成功。 而socket.TCPSocket是支持指定网卡的,且提供了一个将 TCP 升级到 TLS 的接口:constructTLSSocketInstance(tcpSocket: TCPSocket)。
是的,又是一些诡异的设计和表现,但恰好能结合以实现我们的诉求,伪代码如下:
1 // 创建 TCP 2 let tcp: socket.TCPSocket = socket.constructTCPSocketInstance(); 3 // TCP bind 目标网卡 IP(虽然 bind 在底层什么都没做,但这一步仍然要做,不然后面 NetHandle bind 也可能出问题) 4 await tcp.bind({ address: cellular_ip }); 5 // 把 TCP bind 到目标网卡(让 TCP 请求时使用目标网卡) 6 await handle.bindSocket(tcp); 7 // TCP 进行连接(不连接无法升级到 TLS) 8 await tcp.connect(options); 9 // TCP 升级到 TLS 10 let tlsSocket = socket.constructTLSSocketInstance(tcp); 11 // TLS 发起连接 12 await tlsSocket.connect(this.attr.connectOptions); 13 // 监听端口(必须在连接成功之后) 14 listenSocket(tlsSocket); 15 // 发送数据 16 await tlsSocket.send(data); 17
《鸿蒙ets实现强制蜂窝网络》 是转载文章,点击查看原文。


