如何确定哪些IIS Express实例正在使用端口?

我想以编程方式杀死正在占用特定端口的正在运行的IIS实例,但似乎无法确定哪些IIS实例正在使用特定端口。

netstat.exe只显示进程正在使用PID 4,但那是系统进程。 “netsh http show urlacl”根本不显示占用的端口。

IIS Express Tray程序以某种方式知道这一点。 当我尝试在端口被占用时启动另一个IIS Express实例时,我收到以下错误:
“端口’40000’已被进程’IIS Express’使用(进程ID’10632’)。

任何人都知道如何获得这些信息?

似乎PID是4(系统),因为实际的侦听套接字在名为http的服务下。

我查看了iisexpresstray.exe用于提供所有正在运行的IISExpress应用程序的列表。 值得庆幸的是,它的托管.NET代码(全部在iisexpresstray.dll中)很容易被反编译。

它似乎至少有三种不同的方法来获取进程的端口号:

  1. 从命令行参数读取/port (我们知道不可靠)
  2. 运行netsh http show servicestate view=requestq并解析输出
  3. 调用Microsoft.Web.RuntimeStatusClient.GetWorkerProcess(pid)并解析站点URL

不幸的是,iisexpresstray.dll中的大多数有用的东西都像IisExpressHelper类一样被声明为internal (尽管我认为有工具可以生成包装器或复制程序集并公开所有内容)。

我选择使用Microsoft.Web.dll。 它在我的GAC中,虽然由于某种原因没有出现在Visual Studio中作为参考添加的程序集列表中,所以我只是从我的GAC复制了该文件。 有了Microsoft.Web.dll,只需要使用这段代码:

  using (var runtimeStatusClient = new RuntimeStatusClient()) { var workerProcess = runtimeStatusClient.GetWorkerProcess(process.Id); // Apparently an IISExpress process can run multiple sites/applications? var apps = workerProcess.RegisteredUrlsInfo.Select(r => r.Split('|')).Select(u => new { SiteName = u[0], PhysicalPath = u[1], Url = u[2] }); // If we just assume one app return new Uri(apps.FirstOrDefault().Url).Port; } 

您还可以调用RuntimeClient.GetAllWorkerProcesses来仅检索实际的工作进程。

我也查看了RegisteredUrlsInfo (在Microsoft.Web.dll中),发现它使用了两个COM接口,

  1. IRsca2_CoreF90F62AB-EE00-4E4F-8EA6-3805B6B25CDD
  2. IRsca2_WorkerProcessB1341209-7F09-4ECD-AE5F-3EE40D921870

最后,我读到了一个版本的Microsoft.Web.Administration显然能够读取IISExpress应用程序信息,但信息非常稀缺,我在系统上找到的那个甚至不允许我在没有管理员权限的情况下实例化ServerManager

以下是@makhdumi在回答中建议的调用netsh.exe的C#实现:

用法:

 static public bool TryGetCurrentProcessRegisteredHttpPort(out List ports, out Exception ex) { NetshInvoker netsh = new NetshInvoker(); return netsh.TryGetHttpPortUseByProcessId(Process.GetCurrentProcess().Id, out ports, out ex); } 

执行:

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace YourCompanyName.Server.ServerCommon.Utility { ///  /// Invoke netsh.exe and extract information from its output. /// Source: @crokusek, https://stackoverflow.com/questions/32196188 /// @GETah, https://stackoverflow.com/a/8274758/538763 ///  public class NetshInvoker { const string NetshHttpShowServiceStateViewRequestqArgs = "http show servicestate view=requestq"; public NetshInvoker() { } ///  /// Call netsh.exe to determine the http port number used by a given windowsPid (eg an IIS Express process) ///  /// For example an IIS Express process ///  ///  ///  public bool TryGetHttpPortUseByProcessId(Int32 windowsPid, out List ports, out Exception ex) { ports = null; try { if (!TryQueryProcessIdRegisteredUrls(out Dictionary> pidToUrlMap, out ex)) return false; if (!pidToUrlMap.TryGetValue(windowsPid, out List urls)) { throw new Exception(String.Format("Unable to locate windowsPid {0} in '{1}' output.", windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs)); } if (!urls.Any()) { throw new Exception(String.Format("WindowsPid {0} did not reference any URLs in '{1}' output.", windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs)); } ports = urls .Select(u => new Uri(u).Port) .ToList(); return true; } catch (Exception ex_) { ex = ex_; return false; } } private bool TryQueryProcessIdRegisteredUrls(out Dictionary> pidToUrlMap, out Exception ex) { if (!TryExecNetsh(NetshHttpShowServiceStateViewRequestqArgs, out string output, out ex)) { pidToUrlMap = null; return false; } bool gotRequestQueueName = false; bool gotPidStart = false; int currentPid = 0; bool gotUrlStart = false; pidToUrlMap = new Dictionary>(); foreach (string line in output.Split('\n').Select(s => s.Trim())) { if (!gotRequestQueueName) { gotRequestQueueName = line.StartsWith("Request queue name:"); } else if (!gotPidStart) { gotPidStart = line.StartsWith("Process IDs:"); } else if (currentPid == 0) { Int32.TryParse(line, out currentPid); // just get the first Pid, ignore others. } else if (!gotUrlStart) { gotUrlStart = line.StartsWith("Registered URLs:"); } else if (line.ToLowerInvariant().StartsWith("http")) { if (!pidToUrlMap.TryGetValue(currentPid, out List urls)) pidToUrlMap[currentPid] = urls = new List(); urls.Add(line); } else // reset { gotRequestQueueName = false; gotPidStart = false; currentPid = 0; gotUrlStart = false; } } return true; } private bool TryExecNetsh(string args, out string output, out Exception exception) { output = null; exception = null; try { // From @GETah, https://stackoverflow.com/a/8274758/538763 Process p = new Process(); p.StartInfo.FileName = "netsh.exe"; p.StartInfo.Arguments = args; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.Start(); output = p.StandardOutput.ReadToEnd(); return true; } catch (Exception ex) { exception = ex; return false; } } } } 

您可以运行以下命令来获取可执行文件及其PID的信息

 netstat -a -n -o -b | find "iisexpress.exe"