Netty
是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。
在 Android
平台,经常用 Netty
来对接物联网设备,处理各种自定义协议的场景。
这不今天碰到个场景,说需要终端在同时连接 蜂窝数据
和 WiFi
的时候,应用程序能够一面使用 WiFi
网络和一些物联网设备通信,一面使用 蜂窝数据
和服务器通信。并且这两种访问是并行的,协同工作。
Android 的网络策略分析
针对 Android 的外观表现,在连接 WiFi
之后,信号棒旁边的数据就自动消失了。难道此时系统已经关闭了蜂窝数据?
为了验证这个猜想,直接进入 shell
查看这种清晰下网卡的状况:
可以看到同时存在两个网卡,一个是蜂窝数据,另一个是 WiFi。
那么刚刚的猜测就否定了,也说明 Android 系统不是靠禁用网卡来决定使用什么网络,猜测可能是用了路由控制等手段。
那这两个网卡现在都还能使用吗?继续使用命令验证一下:
这里用了 http://2024.ip138.com/ 来回显请求 IP,可以看到使用不同的网卡回显的 IP 是不一样的。
也证明此时两张网卡都是能用的。
Android 应用如何使用不同的网络
网卡都能用,那么应用有办法向上面命令行那样指定某个网卡发出出站请求吗?
仅仅使用 Java
原生的方案,或许可以但兼容性无法保证。
不如来看看 Android 官方给出的解决方案:
https://developer.android.com/reference/android/net/ConnectivityManager#requestNetwork(android.net.NetworkRequest,%20android.net.ConnectivityManager.NetworkCallback)
Android Framework
留了一个 public void requestNetwork (NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)
给应用,用于向系统申请某种类型的网络,就绪后在 NetworkCallback
回调回来一个 Network
类:
https://developer.android.com/reference/android/net/Network
这个类没有太多有价值的方法,其中有一个 public SocketFactory getSocketFactory ()
方法,返回的 SocketFactory
能够和具体类型的网络关联。
换句话说,这个 Network
中 SocketFactory
创建的 Socket
就能够实现从具体的网卡发送接收数据。
接下来就看看应用的支持情况。
应用支持情况
Java
Java
的各种类没啥好说的,肯定是兼容 SocketFactory
的。重点是看看常用的三方库的支持情况。
OkHttp
Android
应用开发里面用的比较多的 OkHttp
库,能够方便的设置自定义的 SocketFactory
,经过测试可以实现从不同网卡收发数据的效果。
演示代码如下:
执行 okhttpCall
方法后控制台打印如下:
Netty
Java
生态中还有一个重量级选手 Netty
,也能在 Android
发挥极大的作用。尤其是各种物联网的场景中。
在 Netty
应用 SocketFactory
就稍微麻烦一点点。
一般情况下,使用 Netty 的目的有两个:
- 借助 Netty 封装的 NIO,实现高性能的服务器。
- 借助 Netty 灵活的协议处理,方便实现各种
Socket
自定义协议封装、解析。
并且通常情况下都是这样用的:
重点是整条路线都是 Nio
,这注定无法和 Java
原生 Socket
两个结合。
经过一番研究,发现可以这样用:
这里是使用 Netty
作为一个 Http
客户端来使用(为了请求 IP 回显的 WEB 服务),其他场景原理是一样。
其中 SocketFactoryChannelFactory
是自定义的,源码也很简单:
原理是通过自定义 channelFactory
来代替 channel
函数的功能。目的是能够向 OioSocketChannel
传入 SocketFactory
的生产出来的 Socket
对象。实现从指定网卡收发数据的效果。
调用 nettyCall
的打印如下:
可以看到,已经实现了从不同网卡发出请求。这里打印和 OkHttp
不一样是因为这个 IP 回显服务做了限流,测试次数过于频繁已经被拉黑了。。。
打完收工
总结,Netty
在 Android
系统应用指定网卡的要点就俩:
- 通过
ConnectivityManager
获取指定网络的Network
对象,进而获得对应的SocketFactory
实例。 - 通过自定义
Netty
的ChannelFactory
来构造使用自定义Socket
实例的OioSocketChannel
实例,整个过程会让Netty
使用Oio
而非Nio
。
另外,上面 DEMO
的 NetworkCallback
只考虑了连接可用的场景,实际使用的时候也要考虑网络连接状态变化等情况。