【实战】ASP.NET Core 6 部署在 ARM 树莓派实现 DDNS 和网络唤醒

家里有个树莓派3B(Raspberry Pi 3B Arm 架构 32 位),放在家里一直在吃灰,由于 .NET Core 程序能跨平台,所以准备写个网站部署到树莓派上面。家里宽带有公网 IP 地址,但是每次重启路由器后 IP 地址都会发生变化,华硕路由器的免费 DDNS 并不太好用(家里 IP 变了,域名解析并没有及时生效)。家里有台 DELL 台式机和 DELL EMC 服务器,所以,准备使用 ASP.NET Core 写一个 DDNS 服务和在线网络唤醒工具。

有了 DDNS 可以及时的获取到家里的外网 IP 地址,使用网络唤醒功能可以唤醒我的 DELL 台式机电脑,方便查阅家中资料。

使用 ASP.NET Core 6 写的网站部署在树莓派已经稳定运行 3 个多月了,没有任何问题。



安装 AspNetCore 运行时

首先,树莓派需要安装 Raspberry Pi OS 系统,教程如下:

树莓派3B(Raspberry Pi 3B)安装 Raspberry Pi OS 系统
https://www.itsvse.com/thread-10052-1-1.html


然后在树莓派上面安装 AspNetCore Runtimes,使用 SSH 登录到树莓派主机中,使用 sudo -i 切换到 root 权限下,创建 /usr/dotnet 文件夹,命令如下:

mkdir -p /usr/dotnet && cd /usr/dotnet

下载 aspnetcore-runtime-6.0.1-linux-arm.tar.gz 并解压,命令如下:

wget https://download.visualstudio.microsoft.com/download/pr/ff3b2714-0dee-4cf9-94ee-cb9f5ded285f/d6bfe8668428f9eb28acdf6b6f5a81bc/aspnetcore-runtime-6.0.1-linux-arm.tar.gz
tar -zxvf aspnetcore-runtime-6.0.1-linux-arm.tar.gz

创建软链接,命令如下:

ln -s /usr/dotnet/dotnet /usr/bin/dotnet

至此,ASP.NET Core 网站所需要的运行环境安装完毕,查看安装的运行时,如下图:



DDNS 动态域名服务

调用 DNSPod 的接口来实现 DDNS 动态域名服务,接口文档:https://docs.dnspod.cn/api/modify-records/

使用 VS 2022 新建一个 ASP.NET Core 6 的 MVC 项目,新建 DNSPodOptions.cs 文件配置调用接口所需的参数,如下:

namespace HomeCloud.Models
{
    public class DNSPodOptions
    {
        public const string Name = "DNSPod";

        public string Url { get;set; }

        public string LoginToken { get;set; }

        public string Format { get; set; }

        public string Domain { get; set; }

        public string RecordId { get; set; }

        public string RecordType { get; set; }

        public string RecordLineId { get; set; }

        public string SubDomain { get; set; }

    }
}

appsettings.json 添加如下配置:

"DNSPod": {
    "Url": "https://dnsapi.cn/Record.Modify",
    "LoginToken": "xxxx",
    "Format": "json",
    "Domain": "itsvse.com",
    "RecordId": "xxxx",
    "RecordType": "A",
    "RecordLineId": "0",
    "SubDomain": "xxxx"
  }

修改 Program.cs 文件,将配置信息映射到 DNSPodOptions 类,代码如下:

builder.Services.Configure<HomeCloud.Models.DNSPodOptions>(
    builder.Configuration.GetSection(HomeCloud.Models.DNSPodOptions.Name));

新建 DDNSWorker.cs 文件,创建后台服务,一分钟获取一次 IP 地址,如果 IP 地址有发生变化,则调用 DNSPod 的接口来更新域名解析。(注意:如果1小时之内,提交了超过5次没有任何变动的记录修改请求,该记录会被系统锁定1小时,不允许再次修改。比如原记录值已经是 1.1.1.1,新的请求还要求修改为 1.1.1.1。)

代码如下:

using HomeCloud.Models;
using Microsoft.Extensions.Options;
using System.Net;
using System.Text;

namespace HomeCloud.Workers
{
    public class DDNSWorker : BackgroundService
    {

        private readonly ILogger<DDNSWorker> _logger;
        private readonly IOptions<DNSPodOptions> _options;
        public static string clientIP = string.Empty;

        public DDNSWorker(ILogger<DDNSWorker> logger, 
            IOptions<DNSPodOptions> options)
        {
            _logger = logger;
            _options = options;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            
            await Task.Run(async () =>
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        var temp = GetClientIP();
                        if (!string.IsNullOrWhiteSpace(temp))
                        {
                            if (!temp.Equals(clientIP))
                            {
                                clientIP = temp;
                                var ddnsRet = UpdateDDNS(_options.Value);
                                _logger.LogInformation($"更新 ddns 结果:{ddnsRet}");
                            }
                        }
                        _logger.LogInformation($"当前 ip 地址:{clientIP}");
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError("获取 ip 异常", ex);
                    }
                    await Task.Delay(60 * 1000, stoppingToken);
                }
            }, stoppingToken);
        }

        /// <summary>
        /// GET请求与获取结果
        /// </summary>
        public static string GetClientIP()
        {
            此处获取到外网 ip 地址返回,为了防止别人调用我的接口,此处代码略!
        }

        /// <summary>
        /// UpdateDDNS
        /// 如果1小时之内,提交了超过5次没有任何变动的记录修改请求,该记录会被系统锁定1小时,不允许再次修改。比如原记录值已经是 1.1.1.1,新的请求还要求修改为 1.1.1.1。
        /// </summary>
        /// <returns></returns>
        public static string UpdateDDNS(DNSPodOptions options)
        {
            using var client = new HttpClient();
            HttpContent content = new StringContent($"login_token={options.LoginToken}&format={options.Format}&sub_domain={options.SubDomain}&domain={options.Domain}&record_id={options.RecordId}&value={clientIP}&record_type={options.RecordType}&record_line_id={options.RecordLineId}");
            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
            var res = client.PostAsync(options.Url, content).Result;
            return res.Content.ReadAsStringAsync().Result;
        }
    }
}

修改 Program.cs 文件,注册后台服务,代码如下:

builder.Host.ConfigureServices(services => {
    services.AddHostedService<DDNSWorker>();
});

完整的 Program.cs 代码,如下:

using HomeCloud.Workers;

var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureServices(services => {
    services.AddHostedService<DDNSWorker>();
});

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.Configure<HomeCloud.Models.DNSPodOptions>(
    builder.Configuration.GetSection(HomeCloud.Models.DNSPodOptions.Name));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();


调试程序,可以正常调用 DNSPod 的接口,如下:


网络唤醒(Wake On LAN)

Wake-on-LAN简称WOL或WoL,中文多译为“网上唤醒”、“远程唤醒”技术。WOL是一种技术,同时也是该技术的规范标准,它的功效在于让已经进入休眠状态或关机状态的计算机,透过局域网(多半为以太网)的另一端对其发令,使其从休眠状态唤醒、恢复成运作状态,或从关机状态转成引导状态。此外,与WOL相关的技术也包括远程下令关机、远程下令重启等相关的遥控机制。

修改 HomeController 控制器,添加网络唤醒的接口,代码如下:

using HomeCloud.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Diagnostics;
using System.Net.Sockets;

namespace HomeCloud.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult WOL(string mac)
        {
            WakeUpCore(FormatMac(mac));
            return Json(new { ret = true });
        }
      
        private static void WakeUpCore(byte[] mac)
        {
            //发送方法是通过UDP
            using UdpClient client = new UdpClient();
            //Broadcast内容为:255,255,255,255.广播形式,所以不需要IP
            client.Connect(System.Net.IPAddress.Broadcast, 50000);
            //下方为发送内容的编制,6遍“FF”+17遍mac的byte类型字节。
            byte[] packet = new byte[17 * 6];
            for (int i = 0; i < 6; i++)
                packet[i] = 0xFF;
            for (int i = 1; i <= 16; i++)
                for (int j = 0; j < 6; j++)
                    packet[i * 6 + j] = mac[j];
            //唤醒动作
            int result = client.Send(packet, packet.Length);
        }

        private static byte[] FormatMac(string macInput)
        {
            byte[] mac = new byte[6];
            string str = macInput;
            //消除MAC地址中的“-”符号
            string[] sArray = str.Split('-');
            //mac地址从string转换成byte
            for (var i = 0; i < 6; i++)
            {
                var byteValue = Convert.ToByte(sArray[i], 16);
                mac[i] = byteValue;
            }
            return mac;
        }
    }
}

编辑 Index.cshtml 页面,添加 MAC 地址输入框和按钮,使用 Ajax 调用接口,代码如下:

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome itsvse.com</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<div class="row">
    <div class="col-12">
       <input class="form-control" placeholder="mac 地址" id="mac" />
    </div>
    <div class="col-12 mt-2">
        <button class="btn btn-block btn-primary" onclick="powerOn();" >唤醒</button>
    </div>
</div>
@section Scripts{
    <script>
        function powerOn()
        {
            var mac =$("#mac").val();
            $.post("/Home/WOL",{mac:mac},function(ret){
                alert("ok");
            });
        }
    </script>
}

启动项目,如下图:



部署在树莓派

使用 VS 2022 发布项目,将发布包上传到树莓派的 /home/pi/wol 文件夹下面,如下图:




在 /etc/systemd/system 文件夹下面新建 nbddns.service 文件,将项目注册成 Linux 服务,如下:

[Unit]
Description=nbddns
After=syslog.target network.target

[Service]
User=root
Group=root
Type=simple
WorkingDirectory=/home/pi/wol
ExecStart=/usr/dotnet/dotnet /home/pi/wol/HomeCloud.dll --urls "http://*:5100"

[Install]
WantedBy=multi-user.target

启动 nbddns 服务,并设置开机自启动,命令如下:

systemctl start nbddns.service && systemctl enable nbddns.service

查看服务状态,如下图:

最后,在路由器设置端口映射,然后通过域名和映射的外网端口访问,我手机使用 4G 网络,直接可以访问到部署在树莓派上面的 ASP.NET Core 应用,如下图:



(完)

posted @ 2022-03-09 22:44  _小渣渣  阅读(919)  评论(2编辑  收藏  举报