【问题标题】:How to figure out what IIS Express instance is using a port?如何确定哪个 IIS Express 实例正在使用端口?
【发布时间】:2015-08-25 05:31:39
【问题描述】:

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

netstat.exe 只是显示该进程的 PID 为 4,但那是系统进程。 "netsh http show urlacl"根本不显示占用的端口。

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

有人知道我如何获得这些信息吗?

【问题讨论】:

  • 你解决过这个问题吗?我有类似的要求,但 PID 4 无济于事。谢谢!

标签: c# iis iis-express


【解决方案1】:

似乎 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_Core (F90F62AB-EE00-4E4F-8EA6-3805B6B25CDD)
  2. IRsca2_WorkerProcess (B1341209-7F09-4ECD-AE5F-3EE40D921870)

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

【讨论】:

    【解决方案2】:

    这里是调用 netsh.exe 的 C# 实现,正如@makhdumi 的回答中所推荐的那样:

    用法:

    static public bool TryGetCurrentProcessRegisteredHttpPort(out List<int> 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
    {
        /// <summary>
        /// Invoke netsh.exe and extract information from its output.
        /// Source: @crokusek, https://stackoverflow.com/questions/32196188
        ///         @GETah, https://stackoverflow.com/a/8274758/538763
        /// </summary>
        public class NetshInvoker
        {
            const string NetshHttpShowServiceStateViewRequestqArgs = "http show servicestate view=requestq";
    
            public NetshInvoker()
            {
            }
    
            /// <summary>
            /// Call netsh.exe to determine the http port number used by a given windowsPid (e.g. an IIS Express process)
            /// </summary>
            /// <param name="windowsPid">For example an IIS Express process</param>
            /// <param name="port"></param>
            /// <param name="ex"></param>
            /// <returns></returns>
            public bool TryGetHttpPortUseByProcessId(Int32 windowsPid, out List<Int32> ports, out Exception ex)
            {
                ports = null;
    
                try
                {
                    if (!TryQueryProcessIdRegisteredUrls(out Dictionary<Int32, List<string>> pidToUrlMap, out ex))
                        return false;
    
                    if (!pidToUrlMap.TryGetValue(windowsPid, out List<string> 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<Int32, List<string>> 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<int, List<string>>();
    
                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<string> urls))
                            pidToUrlMap[currentPid] = urls = new List<string>();
    
                        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;
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 这不会输出任何东西。就像我在我的问题中提到的,该端口实际上被进程 ID 4 占用,也就是系统。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 2011-08-21
      • 2011-04-04
      相关资源
      最近更新 更多