【问题标题】:Powershell AcceptTcpClient() cannot be interrupted by Ctrl-CPowershell AcceptTcpClient() 不能被 Ctrl-C 打断
【发布时间】:2020-07-08 19:04:54
【问题描述】:

我正在使用 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)

【问题讨论】:

    标签: powershell tcp client control-c


    【解决方案1】:

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

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

    有关讨论和背景信息,请参阅 this GitHub issue


    至于可能的解决方法

    • 最好的方法 - 如果可能的话 - 是your own answer 中显示的方法:

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

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

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

        • 输入和输出不会是实时对象。
        • 复杂对象(除了原始 .NET 类型和一些知名类型的实例之外的对象)将是原始对象的模拟;本质上,对象具有属性值的静态副本,并且没有方法 - 有关背景信息,请参阅 this answer

    这是一个简单的演示:

    # 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,你会看到:

    【讨论】:

      【解决方案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)
      

      【讨论】:

        猜你喜欢
        • 2017-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-12
        • 2014-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多