【终极版】利用阿里云云解析API实现动态域名解析(ddns),搭建私有服务器【含可执行文件和源码】
阅读原文时间:2023年07月08日阅读:18

前言:懒人可以直接往下看,有代码和全部资源可以下载。此文章是先前文章的定时版,主要是添加了定时执行的功能,并且将代码中的配置项放置到了app.config文件中,方便不懂开发的朋友修改使用。未经许可请勿转载,如有疑问,请联系作者:yxxrui@163.com

我遇到的问题:公司的网络没有固定的公网IP地址,但是需要能够保证的是,每次动态分配的IP地址均为独立的公网IP,只是每次分配后会在1-2天后更换,或者重启路由器后更换,为了能够把这个公网IP利用起来,大家常用的是使用花生壳之类的软件来操作,我使用过花生壳的多款产品,挺好的,就是有一下几个缺点:

1、需要使用花生壳上购买的域名才行,或者使用免费的壳域名,其丑无比

2、不稳定,也很慢

3、限制流量,超流量需要收费

4、限制端口,特定端口需要收费

5、需要在电脑上运行一个花生壳的软件

解决方案:但是为了解决问题,我开始摸索自己搞定它,公司域名从阿里云上购买,阿里云提供云解析技术免费版,提供通过api来管理域名的功能,当我看到该文档的时候直接就看到了希望,解决起来绝对没问题了。首先需要确认一下,有两种情况:

1、公司服务器直接接入网线,这个时候服务器的IP就是公网IP

2、公司服务器链接公司路由器,路由器链接网线,这个时候路由器上是公网IP,而服务器上是局域网IP

第一种情况最简单了,不需要设置什么了,第二种情况需要到路由器设置界面设置端口转发或者直接将全部端口映射到内网的服务器IP上,这样的话,访问公网IP的某个端口时相当于访问内网IP的映射端口,比如路由器IP为125.120.83.30,内网IP为192.168.168.168。可以将路由器中80端口映射到192.168.168.168的80端口上。

到阿里云控制台申请accessKeyIdaccessKeySecret,用来调用API的,为了简单起见,先给域名添加一条解析记录,这样就不需要在程序里判断该域名是否已创建,然后可以直接修改解析记录了。比如我的域名为yxxrui.cn,记录值为blog,也就是要把子域名blog.yxxrui.cn指向我自己电脑125.120.83.30上。

下边直接贴出代码:

创建window service用于定时执行解析任务,在Service的OnStart方法中添加:

MainProgram.MyTimerTask();

其中MainProgram.cs为:

1 using System;
2 using System.Configuration;
3 using System.Net;
4 using System.Text;
5 using System.Text.RegularExpressions;
6 using System.Threading;
7 using Aliyun.Acs.Alidns.Model.V20150109;
8 using Aliyun.Acs.Core;
9 using Aliyun.Acs.Core.Profile;
10
11 namespace AliyunDynamicDomain
12 {
13 ///

14 /// 修改时间:2018-11-18 15 /// 为了方便,特意改写为window service,按时刷新任务 16 /// 不再需要手动设置window自带的定时任务 17 /// 作者:杨浩瑞 yxxrui@163.com 18 ///
19 class MainProgram
20 {
21 #region 获取基本参数和类属性
22 private static readonly string RegionId = GetConfig("regionId");
23 private static readonly string AccessKeyId = GetConfig("accessKeyId");
24 private static readonly string AccessKeySecret = GetConfig("accessKeySecret");
25 private static string _recordId = GetConfig("recordId");
26 private static readonly string Domain = GetConfig("domain");
27 private static readonly string Rr = GetConfig("rr");
28 private static readonly string IpServer = GetConfig("IpServer");
29 private static int _ttl = 600;
30 private static int _waitTime = 60;
31 private static readonly IClientProfile ClientProfile = DefaultProfile.GetProfile(RegionId, AccessKeyId,
32 AccessKeySecret);
33 private static readonly IAcsClient Client = new DefaultAcsClient(ClientProfile);
34
35 private static readonly object TimerLocker = new object();
36 private static Timer _myTimer;
37 #endregion
38
39 #region 定时任务
40 public static void MyTimerTask()
41 {
42 if (!int.TryParse(GetConfig("TTL"), out _ttl))
43 {
44 _ttl = 600;
45 }
46 if (!int.TryParse(GetConfig("WaitTime"), out _waitTime))
47 {
48 _waitTime = 60;
49 }
50 if (_myTimer != null)
51 {
52 return;
53 }
54 lock (TimerLocker)
55 {
56 if (_myTimer == null)
57 {
58 //第一次开始的时间,1秒后开始执行
59 var startTime = DateTime.Now.AddSeconds(1);
60 var delayTime = (startTime - DateTime.Now);
61 var intervalTime = new TimeSpan(0, 0, 0, _waitTime); //10秒执行一次
62 TimerCallback timerDelegate = DealFun;
63 _myTimer = new Timer(timerDelegate, null, delayTime, intervalTime);
64 }
65 }
66 }
67 #endregion
68
69 #region 执行程序,解析域名
70
71 public static void DealFun(object obj)
72 {
73 try
74 {
75 if (string.IsNullOrWhiteSpace(_recordId))
76 {
77 //如果没有获取过,那么获取一下,然后保存起来
78 _recordId = GetRecordId(Domain, Rr);
79 SaveConfig("recordId", _recordId);
80 }
81 if (string.IsNullOrWhiteSpace(_recordId))
82 {
83 LogHelper.Error("recordId获取失败");
84 return;
85 }
86 //获取成功,并且有变化时
87 string lastIp = GetConfig("lastIP");
88 string curIp = GetIp();
89 if (!curIp.Equals("") && !lastIp.Equals(curIp))
90 {
91 var request = new UpdateDomainRecordRequest
92 {
93 RecordId = _recordId,
94 RR = Rr,
95 Type = "A",
96 Value = curIp,
97 TTL = _ttl,
98 Priority = 10
99 };
100 Client.DoAction(request);
101 SaveConfig("lastIP", curIp);
102 LogHelper.Info("刷新成功");
103 return;
104 }
105 LogHelper.Info("与上次IP一致,无需刷新:" + curIp);
106 }
107 catch (Exception e)
108 {
109 LogHelper.Error(e.ToString());
110 }
111 }
112
113 #endregion
114
115 #region 保存配置信息
116 /// 117 /// 保存配置信息 118 ///
119 ///
120 ///
121 private static void SaveConfig(string name,string value)
122 {
123 var cfa = ReadAppConfig();
124 cfa.AppSettings.Settings[name].Value = value;
125 cfa.Save();
126 }
127 #endregion
128
129 #region 获取配置项
130 /// 131 /// 获取配置项 132 ///
133 ///
134 ///
135 private static string GetConfig(string name)
136 {
137 try
138 {
139 var cfa = ReadAppConfig();
140 return cfa.AppSettings.Settings[name].Value;
141 }
142 catch
143 {
144 return "";
145 }
146 }
147 #endregion
148
149 #region 调用阿里云接口获取域名的唯一记录ID
150 /// 151 /// 调用阿里云接口获取域名的唯一记录ID 152 ///
153 /// 域名
154 /// 子域名
155 ///
156 private static string GetRecordId(string domain,string rr)
157 {
158 DescribeDomainRecordsRequest reqq = new DescribeDomainRecordsRequest
159 {
160 DomainName = domain,
161 RRKeyWord = rr
162 };
163 var rss = Client.GetAcsResponse(reqq);
164 foreach (var record in rss.DomainRecords)
165 {
166 if (record.DomainName.Equals(domain) && record.RR.Equals(rr))
167 {
168 return record.RecordId;
169 }
170 }
171 return "";
172 }
173 #endregion
174
175 #region 获取公网IP
176 /// 177 /// 获取当前电脑的真实外网IP地址,可以自行修改成稳定的 178 ///
179 ///
180 private static string GetIp()
181 {
182 var myWebClient = new WebClient { Credentials = CredentialCache.DefaultCredentials };
183 //获取或设置用于向Internet资源的请求进行身份验证的网络凭据
184 var pageData = myWebClient.DownloadData(IpServer);//从指定网站下载数据
185 var all = Encoding.Default.GetString(pageData); //如果获取网站页面采用的是GB2312,则使用这句
186 var regex = @"((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))";
187 var m = Regex.Match(all, regex);
188 if (m.Success)
189 {
190 return m.Value;
191 }
192 return "";
193 }
194 #endregion
195
196 #region 获取配置文件
197 private static Configuration _config;
198 private static Configuration ReadAppConfig()
199 {
200 if (_config == null)
201 {
202 string configPath = AppDomain.CurrentDomain.BaseDirectory+@"/app.config";
203 _config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
204 {
205 ExeConfigFilename = configPath
206 }, ConfigurationUserLevel.None);
207 }
208 //MessageBox.Show(config.AppSettings.Settings["serverIp"].Value);
209 return _config;
210 }
211 #endregion
212 }
213 }

为了方便修改参数,特意将所有用到的参数放到了app.config中,该文件内容如下:


¨C41C

之前的版本中,没有做定时任务,只做了以下的工作,但是近期发现使用者还是蛮多的,所以本次终极版专门做了定时任务功能。

1、安装window service,然后会自定启动定时任务

2、获取当前电脑真实公网IP

3、查看当前IP是否有变化

4、若无变化直接退出,若有变化则修改域名解析设置

5、等待WaitTime秒后,跳回步骤 2 继续执行。

此处讲一下如何安装window service,我在安装包装放了一个install.bat,尽量右键  以管理员身份运行,同时主要别被安全软件拦截了。

欢迎关注和赞赏哦!

原文和文件地址:https://www.yxxrui.cn/article/232.shtml