Сегодня, помимо моего любимого занятия (возиться с Arduino в моем кружке детского творчества), решил я поставить себе сервер (Windows 2012 r2) и использовать его для различных манипуляций. Изучив тонну информации, всё прошло гладко.
Когда, закончил экспериментировать в локальной сети, у меня появился вопрос: «Как можно увидеть свой сервер из внешней сети, если у меня динамический IP». Снова помогла всемирная книга знаний и были найдены такие сервисы, как DynDNS, no-ip и т.п.
После регистрации увидел, что нужно качать прогу (в роутере настроек под dyndns нет), а как добросовестный параноик, я не люблю ставить сторонний софт. Вспомнив, что имею при себе домен второго уровня делегированный на Яндексе, принялся изучать сторону вопроса, для написания своего софта на C#.
За основу я взял статью «Самодельный Dynamic DNS». Для отправки запросов к API был написан следующий метод:
static string GET(String getString)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(getString);
request.Method = "GET";
String test = String.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
test = reader.ReadToEnd();
reader.Close();
dataStream.Close();
}
return test;
}
Теперь нужно было как-то получить внешний IP, а так, как я решил напрочь не пользоваться DynDNS, то и к страничке checkip.dyndns.org решил не обращаться. Начал искать у того же Яндекса. Отправив гет-запрос на страничку yandex.com/internet, нашёл интересную строчку ipv4.internet.yandex.net/internet/api/v0/ip в ответ на такой запрос, мне выдало красивейший IP в чистом виде.
Для понимания откуда взят токен и id, пожалуйста, обратитесь к статье, которую я взял за основу. Таким образом сложились следующие строчки:
string ip = GET("http://ipv4.internet.yandex.net/internet/api/v0/ip").Trim('"');
string respons = GET("https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token="
+ token + "&domain="
+ domain + " &subdomain="
+ subdomain + "&record_id="
+ id + "&content="
+ ip);
Наконец т.к. всё это дело должно работать на сервере, я решил пересоздать консольное приложение в службу. Были добавлены таймеры и проверка на изменение ip относительно предыдущего.
using System;
using System.ServiceProcess;
using System.IO;
using System.Net;
using System.Timers;
namespace DDNSyapi
{
public partial class Service1 : ServiceBase
{
String outIp = "";
Timer timer1;
public const string token = "YOURTOKEN";
public const string domain = "YOURDOMAIN";
public const string subdomain ="UPDATINGSUBDOMAIN";
public const string id = "id";
//take token on pddimp.yandex.ru/get_token.xml?domain_name=YOURDOMAIN
//take id on pddimp.yandex.ru/nsapi/get_domain_records.xml?token=YOURTOKEN&domain=YOURDOMAIN
public Service1()
{
InitializeComponent();
}
public static void Logs(string err)
{
StreamWriter txtIst = new StreamWriter(System.IO.Path.GetDirectoryName(
System.Environment.GetCommandLineArgs()[0]) +
"//IpServerLog_" + DateTime.Now.Year +
DateTime.Now.Month + DateTime.Now.Day +
".txt", true);
txtIst.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " : " + err);
txtIst.Close();
}
protected override void OnStart(string[] args)
{
timer1 = new System.Timers.Timer(30 * 60 * 1000);
timer1.Elapsed += timer1_Tick;
timer1.Start();
timer1.Enabled = true;
Logs("Запуск службы");
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
try
{
string ip = GET("http://ipv4.internet.yandex.net/internet/api/v0/ip").Trim('"');
if (!ip.Equals(outIp))
{
outIp = ip;
string respons = GET("https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token="
+ token + "&domain="
+ domain + " &subdomain="
+ subdomain + "&record_id="
+ id + "&content="
+ ip);
Logs(respons);
}
}
catch
{
Logs("проблемы с интернетом..");
}
timer1.Enabled = true;
}
static string GET(String getString)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(getString);
request.Method = "GET";
String test = String.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
test = reader.ReadToEnd();
reader.Close();
dataStream.Close();
}
return test;
}
protected override void OnStop()
{
timer1.Enabled = false;
timer1.Stop();
timer1 = null;
Logs(DateTime.Now.ToString("HH:mm:ss") + " - " + "Остановка службы");
}
}
}
В данный момент при записи нового ip очень много грязнит в лог — можно либо после отладки закомментировать вовсе, либо можно парсить по тегу Error error, тогда можно будет получать чистый ответ от API. Я это не делал, т.к. IP меняется очень редко, а когда всё отлажено, то и ошибки API будут редко.
Таким образом, берём внешний ip мы уже не с постороннего ресурса, а с того-же, где пользуемся доменом, так же код можно запустить на любой windows-машине, вплоть до домашнего компьютера. Но советую тогда переделать проверку IP, так, как сделал автор, на которого я ссылался: ему посоветовали сохранять ip в отдельный файл и брать из него последний, сравнивать с нынешним. Я не делал это потому, что у моего сервера аптайм довольно высокий и перезапуск службы не будет особо часто зачищать переменную outIP.
На этом всё, надеюсь кому-нибудь пригодится сие решение. Жду ваших комментариев и критики.
Автор: sergix