实现一个监控 IP 的 windows 服务
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现一个监控 IP 的 windows 服务相关的知识,希望对你有一定的参考价值。
实现一个监控 IP 的 windows 服务
Intro
我们公司的 VPN 用自己的电脑连公司的台式机的时候需要用 IP 地址,有一次尝试去连的时候发现连不上,第二天到公司发现 IP 变掉了,不是之前连的 IP 了,于是就想写一个简单 Windows 服务来监控台式机的 IP 变化
Overview
在 C# 里我们可以使用 Dns.GetHostAddresses()
方法来获取 IP 地址,我们可以每隔一段时间就判断一下当前的 IP 地址,为了方便测试,可以把这个时间定义在配置里,这样本地开发的时候比较方便
为了避免消息太多,我们可以做一个简单的检查,如果 IP 地址不变,就不发消息了,只有当 IP 信息变化的时候再发消息
我们办公使用的是 Google Chat, 所以打算使用 Google Chat 来发消息,也可以根据需要改成自己想用的通知方式
Implement
首先我们可以新建一个 worker 服务,使用 dotnet cli 新建即可
dotnet new worker -n IpMonitor
如果不习惯没有解决方案文件,也可以新建一个解决方案文件并将项目添加到解决方案文件中
cd IpMonitor
dotnet new sln
dotnet sln add ./IpMonitor.csproj
然后我们来改造我们的 Worker
, Worker
其实就是一个后台服务,我们的服务比较简单就直接在上面改了
public sealed class Worker : BackgroundService
private readonly TimeSpan _period;
private readonly INotification _notification;
private readonly ILogger<Worker> _logger;
private volatile string _previousIpInfo = string.Empty;
public Worker(IConfiguration configuration, INotification notification, ILogger<Worker> logger)
_notification = notification;
_logger = logger;
_period = configuration.GetAppSetting<TimeSpan>("MonitorPeriod");
if (_period <= TimeSpan.Zero)
_period = TimeSpan.FromMinutes(10);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
using var timer = new PeriodicTimer(_period);
while (await timer.WaitForNextTickAsync(stoppingToken))
try
var host = Dns.GetHostName();
var ips = await Dns.GetHostAddressesAsync(host, stoppingToken);
var ipInfo = $"Environment.MachineName - host\\n ips.Order(new IpAddressComparer()).Select(x => x.MapToIPv4().ToString()).StringJoin(", ")";
if (_previousIpInfo == ipInfo)
_logger.LogDebug("IpInfo not changed");
continue;
_logger.LogInformation("Ip info: IpInfo", ipInfo);
await _notification.SendNotification(ipInfo);
_previousIpInfo = ipInfo;
catch (Exception e)
_logger.LogError(e, "GetIp exception");
这里我们使用了 .NET 6 引入的 PeriodicTimer
来实现定时任务,自定义了一个 IpAddressComparer
来对 IP 地址做一个排序,实现如下:
public sealed class IpAddressComparer: IComparer<IPAddress>
public int Compare(IPAddress? x, IPAddress? y)
if (ReferenceEquals(x, y)) return 0;
if (ReferenceEquals(null, y)) return 1;
if (ReferenceEquals(null, x)) return -1;
var bytes1 = x.MapToIPv4().ToString().SplitArray<byte>(new [] '.' );
var bytes2 = y.MapToIPv4().ToString().SplitArray<byte>(new [] '.' );
for (var i = 0; i < bytes1.Length; i++)
if (bytes1[i] != bytes2[i])
return bytes1[i].CompareTo(bytes2[i]);
return 0;
通知使用了 Google Chat 的 webhook API,可以自定义一个 Space
,添加一个 webhook 即可,添加成功即可获取一个 webhook URL, 发送消息 API 可以参考文档:https://developers.google.com/chat/api/guides/message-formats/basic
实现如下:
public sealed class GoogleChatNotification: INotification
private readonly HttpClient _httpClient;
private readonly string _webhookUrl;
public GoogleChatNotification(HttpClient httpClient, IConfiguration configuration)
_httpClient = httpClient;
_webhookUrl = Guard.NotNullOrEmpty(configuration.GetAppSetting("GChatWebHookUrl"));
public async Task<bool> SendNotification(string text)
using var response = await _httpClient.PostAsJsonAsync(_webhookUrl, new text );
return response.IsSuccessStatusCode;
在 Program
文件中注册我们新加的服务就可以了
然后我们进行一些改造来发布和部署 Windows 服务,可以按照文档的提示将项目发布为单文件,部署我比较喜欢 powershell,写了两个简单的 powershell script 来安装和卸载 Windows 服务
首先我们可以在项目里添加 Microsoft.Extensions.Hosting.WindowsServices
的引用,并添加一些发布属性
<PropertyGroup>
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
在 Program
中注册 windows 服务相关配置
using IpMonitor;
Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
services.AddHostedService<Worker>();
services.AddSingleton<HttpClient>();
services.AddSingleton<INotification, GoogleChatNotification>();
)
#if !DEBUG
// https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
.UseWindowsService(options =>
options.ServiceName = "IpMonitor";
)
#endif
.Build()
.Run();
安装服务 powershell 脚本:
$serviceName = "IpMonitor"
Write-Output "serviceName: $serviceName"
dotnet publish -c Release -o out
$destDir = Resolve-Path ".\\out"
$ipMonitorPath = "$destDir\\IpMonitor.exe"
Write-Output "Installing service... $ipMonitorPath $destDir"
New-Service $serviceName -BinaryPathName $ipMonitorPath
Start-Service $serviceName
Write-Output "Service $serviceName started"
卸载服务 powershell 脚本:
$serviceName = "IpMonitor"
Stop-Service $serviceName
Write-Output "Service $serviceName stopped"
Remove-Service $serviceName
Write-Output "Service $serviceName removed"
运行效果如下(脚本运行需要以管理员权限运行):
我们可以使用 Get-Service IpMonitor
来查看服务状态
也可以在任务管理器和服务中查看
最后再把我们的服务卸载掉
More
发布为 Windows 服务时如果有问题可以通过 event log 来排查,在 event log 里可以看到我们服务的日志
References
https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
https://github.com/WeihanLi/SamplesInPractice/tree/master/IpMonitor
https://developers.google.com/chat/api/guides/message-formats/basic
以上是关于实现一个监控 IP 的 windows 服务的主要内容,如果未能解决你的问题,请参考以下文章