将抓包存日志脚本转为Windows服务启动失败问题排查
相关代码与报错信息
启动失败的Windows服务代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.IO; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; using CapturingAndParsingPackets; using PacketDotNet; using SharpPcap; namespace CaptureService { public partial class Service1 : ServiceBase { private static bool _stopCapturing; string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);//获取桌面路径 string filename = DateTime.Now.ToString("yyyy-MM-dd--HH-mm-ss");//用日期命名文件 public Service1() { InitializeComponent(); var devices = CaptureDeviceList.Instance; //获取本地设备 if (devices.Count < 1) { OnStop(); return; } } protected override void OnStart(string[] args) { var devices = CaptureDeviceList.Instance; //获取本地设备 //设置输出类型 var defaultOutputType = StringOutputType.Normal; var outputTypeValues = Enum.GetValues(typeof(StringOutputType)); StringOutputType selectedOutputType = defaultOutputType; int userSelectedOutputType; userSelectedOutputType = 3; selectedOutputType = (StringOutputType)userSelectedOutputType; //读取本地设备 var device = devices[3]; //读取数据包 var readTimeoutMilliseconds = 1000; device.Open(DeviceModes.Promiscuous, readTimeoutMilliseconds); //设置过滤规则 string filter = "host 192.168.0.212"; device.Filter = filter; PacketCapture e; var status = device.GetNextPacket(out e); var rawCapture = e.GetPacket(); // 使用PacketDotNet解析数据包并输出高级信息 var p = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data); // 在桌面创建日志文件并写入日志 using (StreamWriter w = File.AppendText(path + "\\" + filename + ".log")) { Log(p.ToString(selectedOutputType) + p.PrintHex(), w); } device.Close(); } public static void Log(string logMessage, TextWriter txtWriter) { try { txtWriter.Write("\r\n日志条目 : "); txtWriter.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString()); txtWriter.WriteLine(); txtWriter.WriteLine(logMessage); txtWriter.WriteLine("============================================================================================================"); } catch (Exception) { } } protected override void OnStop() { using (StreamWriter w = File.AppendText(path + "\\" + filename + ".log")) { Log("服务于 " + DateTime.Now + " 停止", w); } } } }
可正常运行的VS控制台抓包脚本
using System; using PacketDotNet; using SharpPcap; using System.IO; using System.Reflection; using log4net; using log4net.Config; namespace CapturingAndParsingPackets { class MainClass { // 用于停止捕获循环的标志 private static bool _stopCapturing; public static void Main(string[] args) { // 打印SharpPcap版本 var ver = SharpPcap.Pcap.SharpPcapVersion; Console.WriteLine("使用SharpPcap {0}的PacketDotNet示例", ver); // 获取设备列表 var devices = CaptureDeviceList.Instance; // 如果没有找到设备,打印错误信息 if (devices.Count < 1) { Console.WriteLine("本机未找到任何设备"); return; } Console.WriteLine(); Console.WriteLine("本机可用设备如下:"); Console.WriteLine("----------------------------------------------------"); Console.WriteLine(); var i = 0; // 打印设备信息 foreach (var dev in devices) { /* 描述信息 */ Console.WriteLine("{0}) {1} {2}", i, dev.Name, dev.Description); i++; } Console.WriteLine(); Console.Write("-- 请选择要捕获的设备: "); Console.WriteLine(); Console.WriteLine("输出详细程度选项"); Console.WriteLine("----------------------------------------------------"); Console.WriteLine(); var defaultOutputType = StringOutputType.Normal; var outputTypeValues = Enum.GetValues(typeof(StringOutputType)); foreach (StringOutputType outputType in outputTypeValues) { Console.Write("{0} - {1}", (int)outputType, outputType); if (outputType == defaultOutputType) { Console.Write(" (默认)"); } Console.WriteLine(""); } Console.WriteLine(); Console.Write("-- 请选择详细程度(或按回车键使用默认值): "); StringOutputType selectedOutputType = defaultOutputType; int userSelectedOutputType; // 固定选择 userSelectedOutputType = 3; selectedOutputType = (StringOutputType)userSelectedOutputType; // 注册取消处理程序,用于中断捕获循环 Console.CancelKeyPress += HandleCancelKeyPress; // 固定选择设备 var device = devices[3]; // 打开设备进行捕获 var readTimeoutMilliseconds = 1000; device.Open(DeviceModes.Promiscuous, readTimeoutMilliseconds); // 过滤规则:捕获目标主机192.168.0.212的流量 // 也可以设置为"filter = 'ip';"作为默认规则 string filter = "host 192.168.0.212"; device.Filter = filter; Console.WriteLine(); Console.WriteLine("-- 正在监听 {0}, 按'ctrl-c'停止...", device.Name); while (_stopCapturing == false) { PacketCapture e; var status = device.GetNextPacket(out e); // 如果GetNextRawPacket()超时,可能返回null数据包,此时应回到循环开头尝试获取下一个数据包 if (status != GetPacketStatus.PacketRead) { // 返回循环开头 continue; } var rawCapture = e.GetPacket(); // 使用PacketDotNet解析数据包并输出高级信息 var p = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data); Console.WriteLine(p.ToString(selectedOutputType) + p.PrintHex()); Console.WriteLine("============================================================================================================"); using (StreamWriter w = File.AppendText("networkTraffic.log")) { Log(p.ToString(selectedOutputType), w); Log(p.PrintHex(), w); } } Console.WriteLine("-- 捕获已停止"); // 打印设备统计信息 Console.WriteLine(device.Statistics.ToString()); // 关闭pcap设备 device.Close(); } static void Log(string logMessage, TextWriter txtWriter) { try { txtWriter.Write("\r\n日志条目 : "); txtWriter.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString()); txtWriter.WriteLine(); txtWriter.WriteLine(logMessage); txtWriter.WriteLine("============================================================================================================"); } catch (Exception) { } } static void HandleCancelKeyPress(object sender, ConsoleCancelEventArgs e) { Console.WriteLine("-- 正在停止捕获"); _stopCapturing = true; // 告知处理程序我们将自行处理关闭,返回后不要终止进程,因为我们需要做一些收尾工作(比如关闭打开的捕获设备) e.Cancel = true; } } }
事件查看器报错信息
错误1064
Application: CaptureTrafficService.exe Framework Version: v4.0.30319 Description: 进程因未处理的异常而终止。 Exception Info: System.IO.FileNotFoundException at CaptureTrafficService.Service1.OnStart(System.String[]) at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(System.Object) at System.ServiceProcess.ServiceBase.Run(System.ServiceProcess.ServiceBase[]) at CaptureTrafficService.Program.Main()
无法启动服务。System.IO.FileNotFoundException: 未能加载文件或程序集“netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b1xxxxxxxxxxx”或它的某一个依赖项。系统找不到指定的文件。 文件名: "netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b1xxxxxxxxxxx" at CaptureTrafficService.Service1.OnStart(String[] args) at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
错误1053(移除while循环后)
Application: CaptureTrafficService.exe Framework Version: v4.0.30319 Description: 进程因未处理的异常而终止。 Exception Info: System.IO.FileNotFoundException Exception Info: System.IO.FileNotFoundException at CaptureService.Service1..ctor() at CaptureService.Program.Main()
解决方案
1. 解决netstandard程序集加载失败问题
(1)确认项目目标框架兼容性
如果Windows服务项目基于.NET Framework,需将目标框架升级至4.8及以上(.NET Framework 4.8完全支持.NET Standard 2.1),低版本框架无法兼容该程序集。
(2)配置程序集绑定重定向
在项目的app.config文件中添加以下配置,解决版本不匹配导致的加载失败:
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="netstandard" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" /> </dependentAssembly> </assemblyBinding> </runtime>
(3)调整NuGet包版本
若使用的SharpPcap、PacketDotNet版本过高,可降级到支持.NET Framework的稳定版本,例如:
- SharpPcap:
4.2.0 - PacketDotNet:
1.4.0
2. 修复Windows服务启动超时(错误1053)
Windows服务的OnStart方法必须在30秒内返回,不能包含阻塞逻辑。需将抓包逻辑移至后台线程执行,修改后的核心代码示例:
using System; using System.ServiceProcess; using System.IO; using PacketDotNet; using SharpPcap; using System.Threading; namespace CaptureService { public partial class Service1 : ServiceBase { private bool _stopCapturing; private Thread _captureThread; private ICaptureDevice _captureDevice; private readonly string _logPath; private readonly string _logFileName; public Service1() { InitializeComponent(); _logPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); // 使用公共目录避免权限问题 _logFileName = DateTime.Now.ToString("yyyy-MM-dd--HH-mm-ss") + ".log"; } protected override void OnStart(string[] args) { // 后台线程执行抓包逻辑,避免阻塞OnStart _captureThread = new Thread(StartCapturing) { IsBackground = true }; _captureThread.Start(); } private void StartCapturing() { try { var devices = CaptureDeviceList.Instance; if (devices.Count < 1) { LogMessage("未找到任何网络设备"); OnStop(); return; } var device = devices[3]; var readTimeoutMilliseconds = 1000; device.Open(DeviceModes.Promiscuous, readTimeoutMilliseconds); device.Filter = "host 192.168.0.212"; _captureDevice = device; var selectedOutputType = (StringOutputType)3; LogMessage("服务已启动,开始捕获流量"); while (!_stopCapturing) { PacketCapture packetCapture; var status = device.GetNextPacket(out packetCapture); if (status != GetPacketStatus.PacketRead) { Thread.Sleep(100); continue; } var rawCapture = packetCapture.GetPacket(); var packet = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data); LogMessage(packet.ToString(selectedOutputType) + packet.PrintHex()); } } catch (Exception ex) { LogMessage("捕获过程中发生错误: " + ex.ToString()); } finally { _captureDevice?.Close(); LogMessage("抓包设备已关闭"); } } protected override void OnStop() { _stopCapturing = true; _captureThread?.Join(5000); // 等待线程退出,最多5秒 LogMessage("服务已停止"); } private void LogMessage(string message) { try { using (var writer = File.AppendText(Path.Combine(_logPath, _logFileName))) { writer.Write("\r\n日志条目 : "); writer.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString()); writer.WriteLine(message); writer.WriteLine("============================================================================================================"); } } catch { } } } }
3. 额外注意事项
- 服务权限:运行服务的账户需具备访问网络设备和写入日志目录的权限,建议使用Local System账户。
- 日志路径:避免使用当前用户桌面路径(服务账户可能无法访问),优先选择公共应用数据目录。
内容的提问来源于stack exchange,提问作者Sam1916
相关产品推荐
相关产品推荐

