2

我正在使用 Powershell 编写一个简单的 TCP/IP 服务器。我注意到 Ctrl-C 不能中断 AcceptTcpClient() 调用。Ctrl-C 在通话后工作正常。我四处搜索,到目前为止没有人报告类似的问题。

可以通过以下简单代码重复该问题。我正在使用带有本机 Powershell 终端的 Windows 10 最新补丁,而不是 Powershell ISE。

$listener=new-object System.Net.Sockets.TcpListener([system.net.ipaddress]::any, 4444)
$listener.start()
write-host "listener started at port 4444"
$tcpConnection = $listener.AcceptTcpClient()
write-host "accepted a client"

这就是我运行它时发生的事情

ps1> .\test_ctrl_c.ps1
listener started at port 4444
(Ctrl-C doesn't work here)
4

2 回答 2

2

(从 PowerShell 7.0 开始)Ctrl-C仅在PowerShell 代码执行时有效,在.NET 方法执行期间无效。

由于大多数 .NET 方法调用执行速度很快,因此问题通常不会出现。

有关讨论和背景信息,请参阅此 GitHub 问题


至于可能的解决方法

  • 最好的方法 -如果可能的话- 是您自己的答案中显示的方法:

    • 在循环中运行,该循环定期轮询条件,在尝试之间休眠,并且仅在满足条件时才调用该方法,这意味着该方法将快速执行而不是无限期地阻塞。
  • 如果这不是一个选项(如果没有可以测试的这种情况),您可以在后台作业中运行阻塞方法,以便它在调用者可以按需终止的子进程中运行;请注意这种方法的局限性,但是:

    • 由于需要在隐藏的子进程中运行新的 PowerShell 实例,后台作业速度慢且占用大量资源。

    • 由于作业的输入和输出的跨进程编组是必要的:

      • 输入和输出不会是活动对象。
      • 复杂对象(原始 .NET 类型实例和一些知名类型之外的对象)将模拟原始对象;本质上,具有属性值的静态副本且没有方法的对象 - 请参阅此答案以获取背景信息。

这是一个简单的演示:

# Start the long-running, blocking operation in a background job (child process).
$jb = Start-Job -ErrorAction Stop {
  # Simulate a long-running, blocking .NET method call.
  [Threading.Thread]::Sleep(5000)
  'Done.'
}

$completed = $false
try {

  Write-Host -ForegroundColor Yellow "Waiting for background job to finish. Press Ctrl-C to abort."

  # Note: The output collected won't be *live* objects, and with complex
  #       objects will be *emulations* of the original objects that have
  #       static copies of their property values and no methods.
  $output = Receive-Job -Wait -Job $jb

  $completed = $true

}
finally { # This block is called even when Ctrl-C has been pressed.

  if (-not $completed) { Write-Warning 'Aborting due to Ctrl-C.' }

  # Remove the background job.
  #  * If it is still running and we got here due to Ctrl-C, -Force is needed
  #    to forcefully terminate it.
  #  * Otherwise, normal job cleanup is performed.
  Remove-Job -Force $jb

  # If we got here due to Ctrl-C, execution stops here.
}

# Getting here means: Ctrl-C was *not* pressed.

# Show the output received from the job.
Write-Host -ForegroundColor Yellow "Job output received:"
$output
  • 如果您执行上述脚本并且按 Ctrl-C,您将看到:

没有 Ctrl-C

  • 如果你按下Ctrl-C,你会看到:

Ctrl-C

于 2020-03-28T02:10:46.330 回答
2

在得到@mklement0 的回答后,我放弃了我原来的干净代码。我想出了一个解决方法。现在 Ctrl-C 可以中断我的程序

$listener=new-object System.Net.Sockets.TcpListener([system.net.ipaddress]::any, 4444)
$listener.start()
write-host "listener started at port 4444"
while ($true) {
   if ($listener.Pending()) {
      $tcpConnection = $listener.AcceptTcpClient()
      break;
   }
   start-sleep -Milliseconds 1000
}
write-host "accepted a client"

现在 Ctrl-C 工作

ps1> .\test_ctrl_c.ps1
listener started at port 4444
(Ctrl-C works here)
于 2020-03-28T18:30:29.233 回答