.NET Core 代碼安裝服務啟動

来源:https://www.cnblogs.com/dotnet-org-cn/archive/2019/09/22/11565702.html
-Advertisement-
Play Games

最近做了一些.NET Core的程式,有在Windows下運行的 有在CentOS 下運行的,Windows下運行的還好,對Windows下還算比較熟悉了,但CentOS 下 每次都是找筆記支持命令 於是今天晚上就乾脆把以.NET Core程式已服務形式啟動的代碼封裝了下,代碼 主要是便於安裝。 我 ...


最近做了一些.NET Core的程式,有在Windows下運行的 有在CentOS 下運行的,Windows下運行的還好,對Windows下還算比較熟悉了,但CentOS 下 每次都是找筆記支持命令

於是今天晚上就乾脆把以.NET Core程式已服務形式啟動的代碼封裝了下,代碼 主要是便於安裝。

我們寫好一個程式後 然後要已服務啟動 每次都是需要輸入命令,對於我這種不記單詞的人來說太痛苦了,

當然windows環境下命令到是很簡單。

廢話不說了 先給大家看下效果

CentOS 下的運行效果是這樣的

dotnet ConsoleApp2.dll -i 

 

進行服務的安裝

dotnet ConsoleApp2.dll -u

進行服務的卸載

 

 

 

 

安裝完成後使用 ps x 進行進程查看 是否存在。

Window 下運行效果

 

 

 安裝完成,我們在到window 服務管理工具去看看

 

 

 

 

發現已經多了一個服務,說明安裝成功了

 

然後再來分析代碼

首先CentOS 下讓程式以服務形式啟動 我們需要做以下工作

1、寫service文件

2、systemctl 啟動service

service文件內容如下:

[Unit]
Description="服務說明"      
[Service]
Type=simple
GuessMainPID=true
WorkingDirectory=//項目路徑
StandardOutput=journal
StandardError=journal
ExecStart=/usr/bin/dotnet 項目文件dll  //啟動指令
Restart=always

[Install]
WantedBy=multi-user.target

參考:http://www.jinbuguo.com/systemd/systemd.service.html

在使用systemctl 命令使其生效

systemctl enable *.service 使自啟動生效
systemctl start *.service 立即啟動項目服務

那麼結合代碼就好說了

 var servicepath = $"/etc/systemd/system/{serviceName}.service";// 創建服務文件
                    System.IO.File.WriteAllText(servicepath, $"[Unit]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Description={serviceDescription}{Environment.NewLine}");// 服務描述
                    System.IO.File.AppendAllText(servicepath, $"[Service]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Type=simple{Environment.NewLine}");//設置進程的啟動類型, 必須設為 simple, forking, oneshot, dbus, notify, idle 之一。
                    System.IO.File.AppendAllText(servicepath, $"GuessMainPID=true{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"WorkingDirectory={workingDirectory}{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"StandardOutput=journal{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"StandardError=journal{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"ExecStart=/usr/bin/dotnet {System.IO.Path.GetFileName(filePath)}{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Restart=always{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"[Install]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"WantedBy=multi-user.target{Environment.NewLine}");
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"enable {serviceName}.service"));
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"start {serviceName}.service"));
                    Console.WriteLine($"Unix 下安裝服務完成,如果失敗請手動執行以下命令完成安裝:");
                    Console.WriteLine($"systemctl enable {serviceName}.service  //使自啟動生效");
                    Console.WriteLine($"systemctl start {serviceName}.service  //立即啟動項目服務");
                    Console.WriteLine($"systemctl status {serviceName}.service -l //查看服務狀態");
View Code

這樣 我們就可以安裝並啟動服務了,

那麼windows下麵 其實根據簡單 ,就一個命令

sc  create  服務名稱 binpath=啟動文件 就可以了

當然 需要添加一個ServiceBase的子類,這裡我搞了一個通用的

代碼如下

/// <summary>
    /// Windows 服務
    /// </summary>
    class WinService : ServiceBase
    {
        private static string WinServiceName;
        private static Action<string[]> StartRun;
        public WinService()
        {
            ServiceName = WinServiceName;
        }
        /// <summary>
        /// 啟動服務
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            StartRun(args);
        }
        public static void Config(Action<string[]> startRun,string serviceName)
        {
            WinServiceName = serviceName;
            StartRun = startRun;
        }
    }
View Code

這裡為什麼是Action<string[]> StartRun; 了 就是因為讓服務啟動後 執行實際要執行的代碼。

比如demo

class Program
    {
        [Description("這是一個測試服務")]
        [DisplayName("測試服務")]
        static void Main(string[] args)
        {
            DotNet.Install.ServiceInstall.Run("test", args, Run);
        }
        static void Run(string[] args)
        {
            HttpServer httpServer = new HttpServer();
            httpServer.Start();
        }
    }
View Code

服務啟動後是執行的Run方法。

WIndows下的安裝代碼那就是

/// <summary>
        /// Windows 環境下運行
        /// </summary>
        /// <param name="filePath">啟動文件</param>
        /// <param name="serviceName">服務名稱</param>
        /// <param name="displayName">顯示名稱</param>
        /// <param name="serviceDescription">服務說明</param>
        /// <param name="args"></param>
        /// <param name="startRun">實際運行方法</param>
        /// <returns></returns>
        static bool RunWin(string filePath, string serviceName,string displayName, string serviceDescription, string[] args, Action<string[]> startRun)
        {
            if (args.Length == 1)
            {
                var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
                if (System.IO.File.Exists(System.IO.Path.ChangeExtension(filePath, ".exe")))//判斷是否存在exe ,如果存在則啟動exe
                {
                    filePath = System.IO.Path.ChangeExtension(filePath, ".exe");//修改尾碼名為exe
                }
                else
                {
                    var dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe");//查找預設軟體安裝目錄下的dotnet.exe
                    if (System.IO.File.Exists(dotnetPath))
                    {
                        filePath = $"\\\"{dotnetPath}\\\" \\\"{filePath}\\\"";
                    }
                    else
                    {
                        dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "dotnet", "dotnet.exe");//為了防止有的人只裝了x86位的,所以查找x86下的軟體安裝目錄下的dotnet.exe
                        if (System.IO.File.Exists(dotnetPath))
                        {
                            filePath = $"\\\"{dotnetPath}\\\" \\\"{filePath}\\\"";
                        }
                        else
                        {
                            Console.WriteLine($"系統無法定位DotNet Core的安裝目錄。");
                            return true;
                        }
                    }
                }
                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                    {
                        return true;
                    }
                    Console.WriteLine(StartProcess("sc.exe", $"create {serviceName} binpath=\"{filePath}\" start=auto DisplayName=\"{displayName}\""));
                    Console.WriteLine($"Windows 下安裝服務完成,如果失敗請手動執行以下命令完成安裝:");
                    Console.WriteLine($"sc create {serviceName} binpath=\"{filePath}\" start=auto DisplayName=\"{displayName}\" //安裝服務");
                    using (var service = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{serviceName}", true))
                    {
                        service.SetValue("Description", serviceDescription);
                    }
                    return true;
                }
                else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                    {
                        return true;
                    }
                    Console.WriteLine(StartProcess("sc.exe", $"delete {serviceName}"));
                    Console.WriteLine($"Windows 下卸載服務完成,如果失敗請手動執行以下命令完成卸載:");
                    Console.WriteLine($"sc delete {serviceName}  //卸載服務");
                    return true;
                }
            }
            WinService.Config(startRun, serviceName);
            using (var service = new WinService())
            {
                System.ServiceProcess.ServiceBase.Run(service);
            }
            return false;
        }
View Code

這樣我們就完美了

在放上整個代碼:

/**************************************************************
* Copyright (C) 2019 www.hnlyf.com 版權所有(盜版必究)
*
* 作者: 李益芬(QQ 12482335)
* 創建時間: 2019/09/22 22:40:15
* 文件名: 
* 描述: 
* 
* 修改歷史
* 修改人:
* 時間:
* 修改說明:
*
**************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Linq;
using System.ComponentModel;
using Microsoft.Win32;

namespace DotNet.Install
{
    /// <summary>
    /// 服務安裝
    /// </summary>
    public static class ServiceInstall
    {
        /// <summary>
        /// Windows 環境下運行
        /// </summary>
        /// <param name="filePath">啟動文件</param>
        /// <param name="serviceName">服務名稱</param>
        /// <param name="displayName">顯示名稱</param>
        /// <param name="serviceDescription">服務說明</param>
        /// <param name="args"></param>
        /// <param name="startRun">實際運行方法</param>
        /// <returns></returns>
        static bool RunWin(string filePath, string serviceName,string displayName, string serviceDescription, string[] args, Action<string[]> startRun)
        {
            if (args.Length == 1)
            {
                var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
                if (System.IO.File.Exists(System.IO.Path.ChangeExtension(filePath, ".exe")))//判斷是否存在exe ,如果存在則啟動exe
                {
                    filePath = System.IO.Path.ChangeExtension(filePath, ".exe");//修改尾碼名為exe
                }
                else
                {
                    var dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe");//查找預設軟體安裝目錄下的dotnet.exe
                    if (System.IO.File.Exists(dotnetPath))
                    {
                        filePath = $"\\\"{dotnetPath}\\\" \\\"{filePath}\\\"";
                    }
                    else
                    {
                        dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "dotnet", "dotnet.exe");//為了防止有的人只裝了x86位的,所以查找x86下的軟體安裝目錄下的dotnet.exe
                        if (System.IO.File.Exists(dotnetPath))
                        {
                            filePath = $"\\\"{dotnetPath}\\\" \\\"{filePath}\\\"";
                        }
                        else
                        {
                            Console.WriteLine($"系統無法定位DotNet Core的安裝目錄。");
                            return true;
                        }
                    }
                }
                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                    {
                        return true;
                    }
                    Console.WriteLine(StartProcess("sc.exe", $"create {serviceName} binpath=\"{filePath}\" start=auto DisplayName=\"{displayName}\""));
                    Console.WriteLine($"Windows 下安裝服務完成,如果失敗請手動執行以下命令完成安裝:");
                    Console.WriteLine($"sc create {serviceName} binpath=\"{filePath}\" start=auto DisplayName=\"{displayName}\" //安裝服務");
                    using (var service = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{serviceName}", true))
                    {
                        service.SetValue("Description", serviceDescription);
                    }
                    return true;
                }
                else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                    {
                        return true;
                    }
                    Console.WriteLine(StartProcess("sc.exe", $"delete {serviceName}"));
                    Console.WriteLine($"Windows 下卸載服務完成,如果失敗請手動執行以下命令完成卸載:");
                    Console.WriteLine($"sc delete {serviceName}  //卸載服務");
                    return true;
                }
            }
            WinService.Config(startRun, serviceName);
            using (var service = new WinService())
            {
                System.ServiceProcess.ServiceBase.Run(service);
            }
            return false;
        }
        /// <summary>
        /// Unix環境下運行
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="serviceName"></param>
        /// <param name="serviceDescription"></param>
        /// <param name="args"></param>
        /// <param name="startRun"></param>
        /// <returns></returns>
        static bool RunUnix(string filePath, string serviceName, string serviceDescription, string[] args, Action<string[]> startRun)
        {
            var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
            if (args.Length == 1)
            {
                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    var servicepath = $"/etc/systemd/system/{serviceName}.service";// 創建服務文件
                    System.IO.File.WriteAllText(servicepath, $"[Unit]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Description={serviceDescription}{Environment.NewLine}");// 服務描述
                    System.IO.File.AppendAllText(servicepath, $"[Service]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Type=simple{Environment.NewLine}");//設置進程的啟動類型, 必須設為 simple, forking, oneshot, dbus, notify, idle 之一。
                    System.IO.File.AppendAllText(servicepath, $"GuessMainPID=true{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"WorkingDirectory={workingDirectory}{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"StandardOutput=journal{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"StandardError=journal{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"ExecStart=/usr/bin/dotnet {System.IO.Path.GetFileName(filePath)}{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"Restart=always{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"[Install]{Environment.NewLine}");
                    System.IO.File.AppendAllText(servicepath, $"WantedBy=multi-user.target{Environment.NewLine}");
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"enable {serviceName}.service"));
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"start {serviceName}.service"));
                    Console.WriteLine($"Unix 下安裝服務完成,如果失敗請手動執行以下命令完成安裝:");
                    Console.WriteLine($"systemctl enable {serviceName}.service  //使自啟動生效");
                    Console.WriteLine($"systemctl start {serviceName}.service  //立即啟動項目服務");
                    Console.WriteLine($"systemctl status {serviceName}.service -l //查看服務狀態");
                    return true;
                }
                else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                {
                    var servicepath = $"/etc/systemd/system/{serviceName}.service";
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"disable {serviceName}.service"));
                    if (System.IO.File.Exists(servicepath))
                    {
                        System.IO.File.Delete(servicepath);
                    }
                    Console.WriteLine($"Unix 下卸載服務完成,如果失敗請手動執行以下命令完成卸載");
                    Console.WriteLine($"systemctl disable {serviceName}.service  //使自啟動失效");
                    Console.WriteLine($"rm -y {servicepath}  //刪除服務文件");
                    return true;
                }
            }
            startRun(args);
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
            return false;
        }
        /// <summary>
        /// 運行程式,如果有命令-i或者-u則執行安裝或卸載,否則執行<paramref name="startRun"/>
        /// <para>請在Main函數中調用,服務顯示名稱請在Main函數增加[DisplayName()]特性,服務說明[Description]特性。</para>
        /// <para>Windiows下需要依賴System.ServiceProcess.ServiceController</para>
        /// <para>-i 表示安裝服務</para>
        /// <para>-u 表示卸載服務</para>
        /// </summary>
        /// <param name="serviceName">服務名稱</param>
        /// <param name="args">啟動程式的參數</param>
        /// <param name="startRun">實際程式運行的函數</param>
        public static void Run(string serviceName, string[] args, Action<string[]> startRun)
        {
            bool finish = false;
            string serviceDescription = serviceName;
            string displayName = serviceName;
            string filePath = string.Empty;
            if (args.Length == 1)
            {
                StackFrame frame = new StackFrame(1);
                if (string.IsNullOrWhiteSpace(serviceName))
                {
                    serviceName=frame.GetMethod().DeclaringType.Assembly.GetName().Name;
                }
                var displayNames = frame.GetMethod().GetCustomAttributes(typeof(DisplayNameAttribute), true);
                if (displayNames.Length > 0)
                {
                    displayName = (displayNames[0] as DisplayNameAttribute).DisplayName;
                }
                var descriptions = frame.GetMethod().GetCustomAttributes(typeof(DescriptionAttribute), true);
                if (descriptions.Length > 0)
                {
                    serviceDescription = (descriptions[0] as DescriptionAttribute).Description;
                }
                filePath = frame.GetMethod().DeclaringType.Assembly.Location;
            }
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                finish = RunWin(filePath, serviceName, displayName, serviceDescription, args, startRun);
            }
            else
            {
                finish = RunUnix(filePath, serviceName, serviceDescription, args, startRun);
            }
        }
        static string StartProcess(string fileName, string arguments)
        {
            string output = string.Empty;
            using (System.Diagnostics.Process process = new System.Diagnostics.Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    UseShellExecute = false,
                    Arguments = arguments,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    WorkingDirectory = Environment.CurrentDirectory,
                    FileName = fileName,
                };
                process.Start();//啟動程式
                process.WaitForExit();//等待程式執行完退出進程
                output = process.StandardOutput.ReadToEnd();
                process.Close();
            }
            return output;
        }
        static bool AdminRestartApp(string filePath,string[] args)
        {
            if (!IsAdmin())
            {
                Console.WriteLine("重新已管理員啟動" + filePath);
                ProcessStartInfo startInfo = new ProcessStartInfo
                {
                    UseShellExecute = true,
                    Arguments = string.Join(" ", args),
                    WorkingDirectory = Environment.CurrentDirectory,
                    FileName = filePath,
                    Verb = "runas"
                };
                try
                {
                    Process.Start(startInfo);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"重新已管理員啟動失敗:{ex}");
                }
                return false;
            }
            return true;
        }
        /// <summary>
        /// 判斷是否是處於Administrator下允許
        /// </summary>
        /// <returns></returns>
        static bool IsAdmin()
        {
            using (System.Security.Principal.WindowsIdentity wi = System.Security.Principal.WindowsIdentity.GetCurrent())
            {
                System.Security.Principal.WindowsPrincipal wp = new System.Security.Principal.WindowsPrincipal(wi);
                return wp.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
            }
        }
    }
}
View Code

最後把dll 上轉到nuget 上面 DotNetCN.Install

源代碼放到github上面:https://github.com/hnlyf/DotNet.Install

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 聲明 :本博客僅僅是一個初學者的學習記錄、心得總結,其中肯定有許多錯誤,不具有參考價值,歡迎大佬指正,謝謝!想和我交流、一起學習、一起進步的朋友可以加我微信Liu__66666666 這是簡單學習一遍之後的記錄,後期還會修改。 一、學習內容 1. "jvm簡介" 2. 記憶體模型 3. 垃圾回收機制 ...
  • 恢復內容開始 目錄 1. 分支結構 1.1 初步介紹 1.2 使用案例 1.3 練習 2.迴圈結構 1.1 初步介紹 1.2 使用案例 目錄 1. 分支結構 1.1 初步介紹 1.2 使用案例 1.3 練習 2.迴圈結構 1.1 初步介紹 1.2 使用案例 1. 分支結構 1.1 初步介紹 1.2 ...
  • ZooKeeper技術的極少以及ZooKeeper集群的搭建 ...
  • 0. 序 我從一生下來就呆在這個昏暗的地方。 我不明白為什麼程式員這麼喜歡 Dark Mode,Brighten Mode 才是我的最愛。聽說最近連 iphone 都開始支持 Dark Mode 了,沒話講。。。說好的絕不妥協呢? 我周圍是熙熙攘攘的函數群,穿插著變數聲明和巨集定義。 在我們這裡,函數 ...
  • 一、前言 應聘IC前端相關崗位時,FIFO是最常考也是最基本的題目。FIFO經常用於數據緩存、位寬轉換、非同步時鐘域處理。隨著晶元規模的快速增長,靈活的system verilog成為設計/驗證人員的基本功。本文從簡易版的同步FIFO開始,熟悉IP設計與驗證的基礎技能。 二、IP設計 FIFO這一IP ...
  • 背景 運維人員反饋一個容器化的java程式每跑一段時間就會出現OOM問題,重啟後,間隔大概兩天後復現。 問題調查 一查日誌 由於是容器化部署的程式,登上主機後使用docker logs ContainerId查看輸出日誌,並沒有發現任何異常輸出。 使用docker stats查看容器使用的資源情況, ...
  • #set指令 #set指令用於向一個變數或者對象賦值。 格式: #set($var = value) LHS是一個變數,不要使用特殊字元例如英文句號等,不能用大括弧括起來。測試發現#set($user.name = 'zhangsan'),#set(${age} = 18)均賦值失敗。 RHS可以是 ...
  • 前言 說實話,這章本來不打算講的,因為配置多數據源的網上有很多類似的教程。但是最近因為項目要用到分庫分表,所以讓我研究一下看怎麼實現。我想著上一篇博客講了多環境的配置,不同的環境調用不同的資料庫,那接下來就將一個環境用到多個庫也就講了。所以才有了這篇文章。 我們先來看一下今天項目的項目結構,在上篇博 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...