【问题标题】:Process.Start without creating a child process (port handle inheritance)?Process.Start 不创建子进程(端口句柄继承)?
【发布时间】:2014-08-31 19:13:00
【问题描述】:

我在使用 TCP 绑定的自托管应用程序中有一个 WCF 服务。如果我从应用程序启动外部进程“commandLineApp”,即使在我的应用程序关闭后仍会继续,那么下次我的应用程序启动 WCF 服务时会遇到问题。

WCF 表示地址/端口已在使用中。如果我在重新启动应用程序之前关闭外部应用程序(根本不使用 WCF 或任何套接字),WCF 服务就会正常启动。

看起来我的应用程序的套接字句柄以某种方式被新进程“commandLineApp”继承,并且在该进程退出之前不会被释放。

如何防止其他进程从我的主应用程序继承句柄(或成为子进程?)?目前我正在使用 Process.Start 启动另一个进程,使用 UseShellExecute 设置为 False,因为我需要同时设置 EnvironmentVarables 和 RedirectStandardOutput/Error。

如果我设置 UseShellExecute = true,我认为子进程设置会被阻止,但是我没有获得我需要的所有功能。

有没有办法解决这个问题?请参阅下面的示例代码。

ProcessStartInfo psi = new ProcessStartInfo();    
psi.FileName = "commandLineApp.exe";
psi.Arguments = "/someParameter";
psi.EnvironmentVariables.Add("...", "...");
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;

Process process = new Process();
process.StartInfo = psi;
process.Start();
// Monitor if process with PID = process.Id is running
// ...

编辑 - 附加信息: 执行“netstat -noa”表示端口被主应用程序的前一个 PID 以 LISTEN 状态使用,但不再有具有该 PID 的进程。一旦我关闭“commandLineApp”,netstat 命令就不再列出该端口。

WCF 服务在主应用退出前是这样关闭的:

try
{
    serviceHost.Close(TimeSpan.FromSeconds(4));
}
catch (Exception)
{
    serviceHost.Abort();
}

【问题讨论】:

  • 您重定向标准 I/O 然后退出?我认为您的设计有些问题......另外请注意,如果双方都没有正常关闭,TCP 连接可以在它们“关闭”后存活几分钟。你确定“父母”真的死了吗?它不是有一些线程在运行吗?你是如何处理关机的?无论如何,您应该明确关闭所有句柄,因此您的关闭听起来不是很优雅......总而言之,您不应该依赖进程退出清理之后 - 您应该自己进行清理!
  • 我敢打赌这与继承的句柄无关,而与不使用SO_REUSEADDR引起的延迟有关
  • 嗨,我等了几个小时,我已经验证了主进程已经终止。执行“netstat -noa”表示该端口被主应用程序的前一个 PID 使用,但不再有具有该 PID 的进程。我手动清理了所有 WCF 服务,关闭时没有留下任何线程。如果我不启动“commandLineApp”,我在重新启动主应用程序时根本没有任何问题,因此它与该过程的启动方式非常相关。 I/O的redirect是从“commandLineApp”获取第一分钟的输出,发现启动问题。
  • @Dark Falcon - 你知道 SO_REUSEADDR 是否可以与 WCF 结合使用吗? WCF 通常会隐藏所有低级套接字选项。

标签: c# .net wcf sockets process


【解决方案1】:

我在上面的评论中错了:SO_REUSEADDR 仅在句柄已关闭时才适用,但似乎套接字句柄确实是由子进程继承的,没有简单的方法可以防止这种情况发生。这似乎是一个非常愚蠢的设计决定,特别是因为有些地方指出,如果安装了任何 LSP,手柄就不能在孩子身上使用。

如果您对WSASocket 的调用有更多的控制权,您也许可以传递WSA_FLAG_NO_HANDLE_INHERIT 标志,但这在WCF 中很难实现。这里有几个其他选项:

选项 #1:Call CreateProcess yourself 并将 FALSE 传递给 bInheritHandles

选项 #2:在设置 WCF(或任何其他套接字)之前创建一个辅助进程。 Communicate with it via named pipes。从这个帮助程序而不是从主进程启动子进程。

【讨论】:

  • 非常感谢!我已经实现了选项#1。唯一的缺点是使用 RedirectStandardOutput 需要 bInheritHandles,但我主要将它用于调试,所以我有一个开关可以根据情况打开或关闭 bInheritHandles。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-02
  • 1970-01-01
相关资源
最近更新 更多