一.应用背景
我们是一个嵌入式设备,需要联网调用一些接口来请求数据,最近在项目开发中遇到这样一个需求:设备可能会在类似校园、政府等地使用,这些地点可能会限制IP访问,需要设置固定IP才可以使用。当看到这个需求的时候我第一反应这个问题好简单啊,这还不分分钟搞定的事,于是我就让我的同事弄了个路由器模拟了一下这种网络环境,把DHCP禁用,只允许部分IP通行。
我们设备是通过一个模块来分配IP的,于是翻出来模块说明书,找到设置WAN口模式的命令,咔咔咔三下五除二,搞定,查询路由器连接,嗯,是我设置的固定IP,嘿嘿嘿,我真是个大聪明,这就搞定了。结果打脸来的太早,访问接口,不通!
二.故障原因
开始以为是路由器配置问题,但是人家电脑自己设置成固定IP上网飞快,这就见了鬼了,后来仔细回忆了下网络请求的原理,啧,还有DNS解析域名这回事呢。正常来说,你的模块如果提供了设置固定IP的功能,那DNS这块肯定会帮你处理,结果我们就赶上这种定制设备的不成熟了。于是找到厂家要求增加DNS设置功能,厂家说好,你们准备订购多少设备来着?300-500,厂家:哦,那您排期慢一些,您就等吧~
三.解决办法
没办法,时间紧任务急,我是开发小能手我来抗(手动狗头),分析问题,现在可以确定的是设置固定IP后,本机默认的DNS失效了,那就从这条线去寻找解决办法吧。
首先确认的是,项目中网络请求的部分,全部是由Okhttp代理的,嗯,那就好办了,不用处处改了。
查阅Okhttp文档可知,人家本来就支持手动设置自己的域名解析方法:
/**
* Sets the DNS service used to lookup IP addresses for hostnames.
*
* If unset, the [system-wide default][Dns.SYSTEM] DNS will be used.
*/
fun dns(dns: Dns) = apply {
if (dns != this.dns) {
this.routeDatabase = null
}
this.dns = dns
}
那就简单的一批了,直接自己写一个域名解析对象,设置一下就OK了,下面直接贴出方法:
/**
* @Author LiuQN
* @Date 2024/8/1 15:24
*/
public class CustomDns implements Dns {
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
LogUtil.e("test_dns", "开启阿里公共DNS解析,目标:" + hostname);
if (hostname.contains("www.baidu.com")) {
DNSResolver.setSchemaType(DNSResolver.HTTP);
} else {
DNSResolver.setSchemaType(DNSResolver.HTTPS);
}
String[] ipArr = DNSResolver.getInstance().getIPsV4ByHost(hostname);
LogUtil.e("test_dns", "开启阿里公共DNS解析,IP:" + BaseJson.toJson(ipArr));
if (0 == ipArr.length) {
return Collections.emptyList();
}
List<InetAddress> inetAddressList = new ArrayList<>(ipArr.length);
for (String ip : ipArr) {
if ("0".equals(ip)) {
continue;
}
try {
InetAddress inetAddress = InetAddress.getByName(ip);
inetAddressList.add(inetAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
return inetAddressList;
}
}
然后初始化OkHttpClient对象:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.dns(new CustomDns());
其中,lookup就是解析域名要手动实现的部分。我这里选择的是阿里的公共解析,每月有1000万次免费解析(不过https一次解析算5次,这就是为啥我里面ping百度的操作指定了使用http模式解析,能省一点是一点),如果不想使用阿里公共DNS解析(不想花钱),那就替换lookup中的方法,手动将你的域名提交到免费DNS解析服务上(比如8.8.8.8,114.114.114.114等),然后将返回的的IP做成InetAddress对象,return即可,这就是DNS解析的原理,不过阿里更快一些吧,预算充足的话可以考虑,稳定、安全、嵌入简单,其实也没多少钱~