如何根据端口号或IP地址获取对应进程名称?(基于C#与SharpPcap库场景)
嘿,针对你提出的两个技术诉求,我来给你详细拆解下实现方法:
一、根据端口号获取进程名称
首先得区分操作系统场景,考虑到你用C#开发,大概率是在Windows环境下,我重点讲Windows的实现,也补充下跨平台的思路:
Windows平台实现
方法1:使用WMI查询(代码简单易上手)
WMI(Windows Management Instrumentation)可以直接关联端口和进程,优点是代码量小,缺点是查询速度稍慢,适合低频查询场景。示例代码:
using System.Management; public static string GetProcessNameByPort(int port) { try { // 查询TCP连接对应的进程 var tcpQuery = new ManagementObjectSearcher($"SELECT OwningProcess FROM Win32_TcpConnection WHERE LocalPort = {port}"); foreach (var obj in tcpQuery.Get()) { uint pid = (uint)obj["OwningProcess"]; var process = System.Diagnostics.Process.GetProcessById((int)pid); return process.ProcessName; } // 查询UDP端点对应的进程 var udpQuery = new ManagementObjectSearcher($"SELECT OwningProcess FROM Win32_UdpEndpoint WHERE LocalPort = {port}"); foreach (var obj in udpQuery.Get()) { uint pid = (uint)obj["OwningProcess"]; var process = System.Diagnostics.Process.GetProcessById((int)pid); return process.ProcessName; } return "未找到对应进程"; } catch (Exception ex) { return $"查询失败:{ex.Message}"; } }
方法2:调用Windows API(高效推荐)
通过GetExtendedTcpTable和GetExtendedUdpTable直接读取系统内核级的端口-PID映射,速度更快,适合高频查询或实时场景。需要先定义P/Invoke结构和方法,示例代码:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; public static class PortProcessMapper { // TCP表结构定义 [StructLayout(LayoutKind.Sequential)] private struct MIB_TCPTABLE_OWNER_PID { public uint dwNumEntries; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public MIB_TCPROW_OWNER_PID[] table; } [StructLayout(LayoutKind.Sequential)] private struct MIB_TCPROW_OWNER_PID { public uint dwState; public uint dwLocalAddr; public uint dwLocalPort; public uint dwRemoteAddr; public uint dwRemotePort; public uint dwOwningPid; } // UDP表结构定义 [StructLayout(LayoutKind.Sequential)] private struct MIB_UDPTABLE_OWNER_PID { public uint dwNumEntries; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public MIB_UDPROW_OWNER_PID[] table; } [StructLayout(LayoutKind.Sequential)] private struct MIB_UDPROW_OWNER_PID { public uint dwLocalAddr; public uint dwLocalPort; public uint dwOwningPid; } // P/Invoke方法声明 [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint pdwSize, bool bOrder, uint ulAf, uint tableClass, uint reserved); [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedUdpTable(IntPtr pUdpTable, ref uint pdwSize, bool bOrder, uint ulAf, uint tableClass, uint reserved); private const uint AF_INET = 2; private const uint TCP_TABLE_OWNER_PID_ALL = 5; private const uint UDP_TABLE_OWNER_PID = 1; // 获取所有TCP端口-PID映射 public static Dictionary<(IPAddress, int), int> GetTcpPortPidMap() { var map = new Dictionary<(IPAddress, int), int>(); uint size = 0; GetExtendedTcpTable(IntPtr.Zero, ref size, false, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); IntPtr tablePtr = Marshal.AllocHGlobal((int)size); try { if (GetExtendedTcpTable(tablePtr, ref size, false, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == 0) { var table = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(tablePtr, typeof(MIB_TCPTABLE_OWNER_PID)); var rows = new MIB_TCPROW_OWNER_PID[table.dwNumEntries]; IntPtr rowPtr = (IntPtr)((long)tablePtr + Marshal.SizeOf(table.dwNumEntries)); for (int i = 0; i < table.dwNumEntries; i++) { rows[i] = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID)); rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(MIB_TCPROW_OWNER_PID))); IPAddress localAddr = IPAddress.Parse($"{(rows[i].dwLocalAddr >> 0) & 0xFF}.{(rows[i].dwLocalAddr >> 8) & 0xFF}.{(rows[i].dwLocalAddr >> 16) & 0xFF}.{(rows[i].dwLocalAddr >> 24) & 0xFF}"); int localPort = (int)((rows[i].dwLocalPort >> 8) | ((rows[i].dwLocalPort & 0xFF) << 8)); map.Add((localAddr, localPort), (int)rows[i].dwOwningPid); } } } finally { Marshal.FreeHGlobal(tablePtr); } return map; } // 获取所有UDP端口-PID映射 public static Dictionary<(IPAddress, int), int> GetUdpPortPidMap() { var map = new Dictionary<(IPAddress, int), int>(); uint size = 0; GetExtendedUdpTable(IntPtr.Zero, ref size, false, AF_INET, UDP_TABLE_OWNER_PID, 0); IntPtr tablePtr = Marshal.AllocHGlobal((int)size); try { if (GetExtendedUdpTable(tablePtr, ref size, false, AF_INET, UDP_TABLE_OWNER_PID, 0) == 0) { var table = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(tablePtr, typeof(MIB_UDPTABLE_OWNER_PID)); var rows = new MIB_UDPROW_OWNER_PID[table.dwNumEntries]; IntPtr rowPtr = (IntPtr)((long)tablePtr + Marshal.SizeOf(table.dwNumEntries)); for (int i = 0; i < table.dwNumEntries; i++) { rows[i] = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_UDPROW_OWNER_PID)); rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(MIB_UDPROW_OWNER_PID))); IPAddress localAddr = IPAddress.Parse($"{(rows[i].dwLocalAddr >> 0) & 0xFF}.{(rows[i].dwLocalAddr >> 8) & 0xFF}.{(rows[i].dwLocalAddr >> 16) & 0xFF}.{(rows[i].dwLocalAddr >> 24) & 0xFF}"); int localPort = (int)((rows[i].dwLocalPort >> 8) | ((rows[i].dwLocalPort & 0xFF) << 8)); map.Add((localAddr, localPort), (int)rows[i].dwOwningPid); } } } finally { Marshal.FreeHGlobal(tablePtr); } return map; } // 根据IP和端口获取进程名称 public static string GetProcessNameByIpPort(IPAddress ip, int port, bool isTcp) { var map = isTcp ? GetTcpPortPidMap() : GetUdpPortPidMap(); if (map.TryGetValue((ip, port), out int pid)) { try { var process = Process.GetProcessById(pid); return process.ProcessName; } catch (Exception) { return $"PID {pid} 对应的进程已退出"; } } return "未找到对应进程"; } }
跨平台思路(Linux/macOS)
如果你的C#程序需要跨平台运行,可以通过调用系统命令并解析输出的方式实现:
- Linux:执行
ss -tulpn命令,提取端口与PID的映射关系 - macOS:执行
lsof -i :<port>命令,解析结果获取进程信息
以Linux为例,示例代码如下:
public static string GetProcessNameByPortLinux(int port) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "ss", Arguments = $"-tulpn | grep :{port}", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); string output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); if (!string.IsNullOrEmpty(output)) { var pidMatch = System.Text.RegularExpressions.Regex.Match(output, @"pid=(\d+)"); if (pidMatch.Success && int.TryParse(pidMatch.Groups[1].Value, out int pid)) { return Process.GetProcessById(pid).ProcessName; } } return "未找到对应进程"; }
二、结合SharpPcap获取TCP/UDP连接对应的进程名称
你已经通过SharpPcap拿到了连接信息,接下来只需要把捕获到的数据包中的IP、端口信息和上面的端口-PID映射关联起来即可,具体步骤如下:
- 定时刷新系统的端口-PID映射(因为网络连接是动态变化的)
- 在SharpPcap的数据包回调中,解析出TCP/UDP的源/目的IP和端口
- 根据连接类型,在映射表中查找对应的PID,再获取进程名称
示例代码:
using SharpPcap; using PacketDotNet; using System; using System.Collections.Generic; using System.Net; class Program { private static Dictionary<(IPAddress, int), int> _tcpMap; private static Dictionary<(IPAddress, int), int> _udpMap; static void Main(string[] args) { // 初始化端口映射,定时每5秒刷新一次 RefreshPortMaps(); var timer = new System.Timers.Timer(5000); timer.Elapsed += (sender, e) => RefreshPortMaps(); timer.Start(); // 获取本地网络设备 var devices = CaptureDeviceList.Instance; if (devices.Count == 0) { Console.WriteLine("未找到可用的网络设备"); return; } // 选择第一个设备开始捕获数据包 using var device = devices[0]; device.OnPacketArrival += Device_OnPacketArrival; device.Open(DeviceMode.Promiscuous, 1000); Console.WriteLine($"开始捕获设备 {device.Name} 的数据包..."); device.StartCapture(); Console.WriteLine("按任意键停止捕获"); Console.ReadKey(); device.StopCapture(); } private static void RefreshPortMaps() { _tcpMap = PortProcessMapper.GetTcpPortPidMap(); _udpMap = PortProcessMapper.GetUdpPortPidMap(); Console.WriteLine("端口-PID映射已刷新"); } private static void Device_OnPacketArrival(object sender, PacketCapture e) { var packet = Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data); var ipPacket = packet.Extract<IpPacket>(); if (ipPacket == null) return; // 处理TCP数据包 var tcpPacket = packet.Extract<TcpPacket>(); if (tcpPacket != null) { IPAddress localIp = ipPacket.SourceAddress; int localPort = tcpPacket.SourcePort; IPAddress remoteIp = ipPacket.DestinationAddress; int remotePort = tcpPacket.DestinationPort; string localProcess = GetProcessName(localIp, localPort, true); // 远程IP+端口的进程信息无法在本地获取,仅作展示 string remoteProcess = "远程进程无法查询"; Console.WriteLine($"TCP连接: {localIp}:{localPort} -> {remoteIp}:{remotePort} | 本地进程: {localProcess}"); return; } // 处理UDP数据包 var udpPacket = packet.Extract<UdpPacket>(); if (udpPacket != null) { IPAddress localIp = ipPacket.SourceAddress; int localPort = udpPacket.SourcePort; IPAddress remoteIp = ipPacket.DestinationAddress; int remotePort = udpPacket.DestinationPort; string localProcess = GetProcessName(localIp, localPort, false); Console.WriteLine($"UDP连接: {localIp}:{localPort} -> {remoteIp}:{remotePort} | 本地进程: {localProcess}"); } } private static string GetProcessName(IPAddress ip, int port, bool isTcp) { var map = isTcp ? _tcpMap : _udpMap; if (map.TryGetValue((ip, port), out int pid)) { try { return System.Diagnostics.Process.GetProcessById(pid).ProcessName; } catch (Exception) { return $"PID {pid} 已退出"; } } return "未知进程"; } }
注意事项
- 运行程序需要管理员权限,因为获取端口-PID映射和网络数据包捕获都需要系统级权限
- 端口映射是动态变化的,建议定时刷新(3-5秒一次),避免获取到过时信息
- 远程IP+端口对应的进程信息无法在本地查询到,通常只关注本地IP+端口关联的进程
内容的提问来源于stack exchange,提问作者Bianca
相关产品推荐
相关产品推荐

