使用C#开发一个类似向日葵的远程工具,想要远程生成及获取服务器的文件,就需要在服务器端运行一个执行程序,这个程序可以是桌面窗口类型、可以是控制台、也可以是Windows服务;现在我使用Windows服务的方式运行在服务器做为服务端执行命令的程序,而且服务运行可以做到自动重启。
1.创建新项目>>Windows服务
2.下一步后可以自定义项目名称、项目生成位置、可以重命名,框架也可以选择最新的4.8,我这里先使用4.7.2,后期有需要也可以在项目属性里选择其他框架;
3.创建成功后会生成一个Service1.cs的类,可以修改名称,我将其改为“ManagerService.cs”,先添加服务安装程序,添加后会出现一个“serviceProcessInstaller1”的和一个“serviceInstaller1”,serviceProcessInstaller1的“Account”属性改为LocalSystem,这个表示服务以系统方式运行,serviceInstaller1的DelayedAutoStar属性改为True,这个让服务可以自动运行
4.以下是ManagerService.cs的完整代码:
using System.ServiceProcess;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Threading;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System;
using System.Threading.Tasks;
using System.IO.Compression;
namespace ManagerService
{
public partial class ServiceMain : ServiceBase
{
private Thread listenerThread;
private bool isRunning = true;
private const int DiscoveryPort = 8888;
private const int CommandPort = 8889;
private string Serialize(object obj)
{
return JsonConvert.SerializeObject(obj);
}
private T Deserialize(string json)
{
return JsonConvert.DeserializeObject(json);
}
public ServiceMain()
{
ServiceLogger.Log($"系统字节序: {(BitConverter.IsLittleEndian ? "Little-Endian" : "Big-Endian")}");
//InitializeComponent();
}
protected override void OnStart(string[] args)
{
EventLog.WriteEntry("ManagerService", "服务已启动,开始监听端口", EventLogEntryType.Information);
listenerThread = new Thread(StartListening);
listenerThread.IsBackground = true;
listenerThread.Start();
}
private void StartListening()
{
// UDP发现监听
new Thread(() =>
{
UdpClient udpServer = new UdpClient(DiscoveryPort);
IPEndPoint remoteEp = new IPEndPoint(IPAddress.Any, 0);
while (isRunning)
{
byte[] data = udpServer.Receive(ref remoteEp);
if (Encoding.ASCII.GetString(data) == "DISCOVERY_REQUEST")
{
byte[] response = Encoding.ASCII.GetBytes("SERVICE_RESPONSE");
udpServer.Send(response, response.Length, remoteEp);
}
}
}).Start();
// TCP命令监听
TcpListener tcpListener = new TcpListener(IPAddress.Any, CommandPort);
tcpListener.Start();
while (isRunning)
{
TcpClient client = tcpListener.AcceptTcpClient();
new Thread(() => HandleClient(client)).Start();
}
}
private async void HandleClient(TcpClient client)
{
ServiceLogger.Log("=== 开始处理新客户端连接 ===");
try
{
using (client)
using (NetworkStream stream = client.GetStream())
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8, false, 1024, leaveOpen: true))
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, leaveOpen: true))
{
string command = await ReadCommandWithTimeout(reader, TimeSpan.FromSeconds(10));
if (string.IsNullOrEmpty(command)) return;
string[] commandParts = command.Split(new[] { '|' }, 2);
if (commandParts.Length != 2) return;
string operation = commandParts[0].Trim().ToUpper();
string path = commandParts[1].Trim();
switch (operation)
{
case "GET_DIR":
await HandleDirectoryRequest(writer, path);
break;
// 新增文件传输处理
case "UPLOAD":
await HandleFileUpload(stream, path);
break;
case "DOWNLOAD":
await HandleFileDownload(writer, path);
break;
default:
await SendErrorResponse(writer, $"不支持的操作: {operation}");
break;
}
}
}
catch (Exception ex)
{
ServiceLogger.Log($"处理客户端异常: {ex.Message}");
}
}
private async Task HandleFileUpload(NetworkStream stream, string savePath)
{
ServiceLogger.Log($"开始处理文件上传: {savePath}");
try
{
if (!ValidatePath(savePath))
{
ServiceLogger.Log($"非法路径:{savePath}");
return;
}
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
// 读取文件长度头
byte[] lengthBytes = await ReadExactAsync(stream, 4);
int fileLength = BitConverter.ToInt32(lengthBytes, 0);
using (FileStream fs = File.Create(savePath))
{
byte[] buffer = new byte[81920];
int totalRead = 0;
while (totalRead < fileLength)
{
int read = await stream.ReadAsync(buffer, 0,
Math.Min(buffer.Length, fileLength - totalRead));
await fs.WriteAsync(buffer, 0, read);
totalRead += read;
ServiceLogger.Log($"已接收 {totalRead}/{fileLength} 字节 ({(double)totalRead / fileLength:P0})");
}
}
// 在文件写入完成后添加成功响应
byte[] successFlag = new byte[] { 0x01 };
await stream.WriteAsync(successFlag, 0, 1);
ServiceLogger.Log($"文件上传完成: {savePath} ({fileLength} 字节)");
}
catch (Exception ex)
{
ServiceLogger.Log($"文件上传失败: {ex.Message}");
throw;
}
}
// 新增文件下载处理方法
private async Task HandleFileDownload(StreamWriter writer, string filePath)
{
ServiceLogger.Log($"开始处理文件下载: {filePath}");
try
{
if (!File.Exists(filePath))
{
await SendErrorResponse(writer, "文件不存在");
return;
}
using (FileStream fs = File.OpenRead(filePath))
{
// 发送文件长度头
byte[] lengthHeader = BitConverter.GetBytes(fs.Length);
await writer.BaseStream.WriteAsync(lengthHeader, 0, 4);
// 发送文件内容
byte[] buffer = new byte[81920];
int bytesRead;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await writer.BaseStream.WriteAsync(buffer, 0, bytesRead);
}
}
ServiceLogger.Log($"文件发送完成: {filePath} ({new FileInfo(filePath).Length} 字节)");
}
catch (Exception ex)
{
await SendErrorResponse(writer, ex.Message);
ServiceLogger.Log($"文件下载失败: {ex.Message}");
}
}
// 添加辅助读取方法
private async Task ReadExactAsync(NetworkStream stream, int count)
{
byte[] buffer = new byte[count];
int totalRead = 0;
while (totalRead < count)
{
int read = await stream.ReadAsync(buffer, totalRead, count - totalRead);
if (read == 0) throw new EndOfStreamException();
totalRead += read;
}
return buffer;
}
// 新增辅助方法:带超时的命令读取
private async Task ReadCommandWithTimeout(StreamReader reader, TimeSpan timeout)
{
var readTask = reader.ReadLineAsync();
var timeoutTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new System.TimeoutException("读取命令超时");
}
return await readTask;
}
private async Task HandleDirectoryRequest(StreamWriter writer, string requestPath)
{
ServiceLogger.Log($"=== 开始处理目录请求: {requestPath} ===");
List responseEntries = new List();
NetworkStream netStream = (NetworkStream)writer.BaseStream;
try
{
// 记录原始请求路径
ServiceLogger.Log($"原始请求路径: {requestPath}");
// 处理驱动器列表请求
if (requestPath == "[DRIVES]")
{
DriveInfo.GetDrives()
.Where(d => d.IsReady)
.ToList()
.ForEach(drive =>
{
responseEntries.Add(new DirectoryEntry
{
Name = $"{drive.Name} ({drive.VolumeLabel})",
FullPath = drive.Name,
IsDirectory = true,
Size = drive.TotalSize
});
});
ServiceLogger.Log($"目录过滤前数量: {responseEntries.Count}");
foreach (var entry in responseEntries.Take(5))
{
ServiceLogger.Log($"[调试] 目录过滤前返回条目: {entry.Name} (路径: {entry.FullPath})");
}
}
// 处理普通目录请求
else
{
// 仅去除末尾分隔符(保留C:\)
string sanitizedPath = Path.GetFullPath(requestPath).Replace('/', '\\').TrimEnd(Path.DirectorySeparatorChar);
// ServiceLogger.Log($"消毒后路径: {sanitizedPath}");
// 特殊处理根目录请求
if (sanitizedPath.EndsWith(":") || sanitizedPath == "")
{
sanitizedPath += Path.DirectorySeparatorChar;
}
ServiceLogger.Log($"请求路径: {requestPath} → 消毒后路径: {sanitizedPath}");
var dirInfo = new DirectoryInfo(sanitizedPath);
if (!dirInfo.Exists)
{
ServiceLogger.Log($"路径不存在: {sanitizedPath}");
throw new DirectoryNotFoundException(sanitizedPath);
}
else
{
// 修改后的目录处理逻辑
foreach (var dir in dirInfo.GetDirectories("*", SearchOption.TopDirectoryOnly)
.Where(d =>
(d.Attributes & FileAttributes.System) == 0 &&
(d.Attributes & FileAttributes.Hidden) == 0 &&
!d.Name.Equals("