在这篇教程我将告诉你如何编写代码更新在著名的动态域名服务提供商之一no-ip服务器上的DNS记录。
动态IP地址
如果您在家里或者移动连接到互联网,你可能会有一个动态的公网IP地址,每次连接它可能会改变。
如果你要和网络上的一个设备联系,你需要知道这个设备的公网IP地址,如果这个地址是动态的,你可能不知道这个IP地址是多少(这样你就无法联系上这个设备)。
动态DNS
在互联网上你可以找到免费的动态DNS(DDNS)服务:他允许你将你选择的域名和一个IP地址绑定,并且可以随时更新IP地址(绑定不同的IP地址)。
更新可以有不同的方法:
- 在你的PC上用一个客户端更新;
- 在你的路由器上应用DDNS功能;
- 让Arduino为我们做这个事情。
NO-IP
应用比较多的动态域名服务商之一是
NO-IP。
如果你注册了NO-IP的
免费服务,你最多可以设置5台主机,使用Hosts/重定向功能。
在这个教程,我注册了以下域名:enctutorial.no-ip.info。
动态域名客户端
每个动态域名客户端,执行以下步骤:
- 得到实际的公网IP。
- 公网IP和动态域名服务器注册的IP地址进行比较。
- 如果不同,通过服务提供商的API更新DNS记录。
代码
完整的代码共享在
GitHub,接下来我将解释它们如何工作。
工作流程
代码的工作流程如下图所示:
作为一个4个状态的有限状态机(
FSM)(也可百度有限状态机):
- 程序启动的时候,它在空闲(IDLE)状态,在给定的时间(CHECK_IP_INTERVAL常数)后,它检查实际的公网IP;
- 为获取公网IP发送请求,程序处于等待响应(WAItiNG_FOR_PUBLIC_IP)状态,如果在给定的时间(REQUEST_TIMEOUT常数)没有收到响应,重发请求,相应的重复尝试次数(attempts)+1,当这个计数器次数大于最大尝试次数(MAX_ATTEMPTS),程序回到空闲(IDLE)状态。
- 程序如果收到响应,获得实际的IP地址,它就和动态域名解析的IP地址进行比较。如果不同,就切换到NOIP_NEEDS_UPDATE状态,否则它返回到空闲(IDLE)状态。
- 如果需要更新,程序发送更新请求到NO-IP服务器并切换到WAITING_FOR_NO_IP状态。如果再给定时间没有收到响应,和WAITING_FOR_PUBLIC_IP状态一样,会再次尝试,达到尝试最大次数,返回空闲(IDLE)状态。如果收到响应,它会返回更新结果,并回到空闲(IDLE)状态。
公网IP
要获得实际的公网IP,我在我的网站写了一个简单的PHP页面:
Arduino浏览这个页面获得实际的公网IP。
DNS记录和比较
程序用dnsLookup()方法获取域名绑定的IP地址。然后将结果转换成一个字符串,与用上面方法获得的实际公网IP地址用compareTo()方法进行比较。
- if(!ether.dnsLookup(noIP_host)) {
- [...]
- } else {
- for(int i = 0; i < 4; i++) {
- dnsIp = dnsIp + String(ether.hisip);
- if(i < 3) dnsIp = dnsIp + ".";
- }
- if(actualIp.compareTo(dnsIp) == 0) {
- Serial.println("No update needed :)");
- [...]
- } else {
- Serial.println("Update needed :(");
- actual_status = STATUS_NOIP_NEEDS_UPDATE;
[color=rgb(51, 102, 153) !important]复制代码
认证
NO-IP在更新DNS记录前要进行身份认证。在代码中定义了一个常量:
这个值是
用户名:密码字符串,必须用base64编码,我们使用在线的
编码器:
测试
为了测试程序的作用,我们手动更新,让这个域名绑定一个错误的IP地址:
然后我们运行程序:
Arduino获得了实际的公网IP,解析了域名绑定的IP,这两个不同,对域名绑定IP成功进行了更新,一段时间后,又进行另外一轮检查,这两个地址没有差异,就没有更新的必要。
重新查看NO-IP网站上的记录,确认进行了更新:
如果认证的密匙错误,程序检测并显示错误: