web-dev-qa-db-ja.com

PowerShellを使用してネットワークポートをより速くテストする

Windows 8.1では、Test-NetConnectionコマンドレットはリモートシステムのネットワークポートの状態を確認するのに役立ちます。ただし、不必要に遅くなることがあります。このプロセスを高速化するために、調整できるいくつかのオプション、または使用できる代替のPowerShellコマンドがあるかどうかを知りたいです。

Test-NetConnectionは、リモートシステムが応答していない場合、結果を返すまでに約10秒かかることがあります。ポートが指定されるたびに、2つの接続テストが実行され、タイムアウトまでに約5秒かかります。最初のテストは、基本的なICMPエコーチェックです。システムがオフラインの場合、またはシステム(または介在するインフラストラクチャ)がICMPエコー要求をブロックするか応答しないように構成されている場合、これはタイムアウトになります。2番目のテストは、指定されたポートに対する実際のチェックです。システムがオフラインの場合、またはポートをブロックしているパスにファイアウォールがある場合、これはタイムアウトになります。

現在の使用例では、リモートシステムは信頼性の高いギガビットイーサネット接続で2ホップしか離れていません。したがって、リクエストの5秒のタイムアウトは非常に長くなります-おそらく30ミリ秒以下のタイムアウトでも信頼できる結果を得ることができます!さらに、システムがオンラインで他のすべてのサービスを利用できる場合でも、システムはICMPエコーに応答しないことがわかっています。したがって、ICMPエコーテストをまったく使用せずに実行でき、TCP接続テストのタイムアウトを減らして、この目的でTest-NetConnectionを使用するスクリプトを高速化できると便利です。

Test-NetConnectionには、これらの動作を変更するオプションがありますか? (詳細なヘルプファイルを読みましたが、答えは「いいえ」のようですが、見落としていることがあった場合は、喜んでお知らせします。)または、PowerShellを使用して同じチェックを実行できる別の方法があります。でももっと速い?

さまざまな理由から、可能な限りオペレーティングシステムに組み込まれている機能を使用するようにスクリプトを制限することを好みます。環境がWindows 8.1の新しいビルドであり、適切なすべてのWindows Updateが適用されており、サードパーティのツールがオプションでないと仮定します。

6
Iszi

非常に基本的(タイムアウト100ミリ秒):

function testport ($hostname='yahoo.com',$port=80,$timeout=100) {
  $requestCallback = $state = $null
  $client = New-Object System.Net.Sockets.TcpClient
  $beginConnect = $client.BeginConnect($hostname,$port,$requestCallback,$state)
  Start-Sleep -milli $timeOut
  if ($client.Connected) { $open = $true } else { $open = $false }
  $client.Close()
  [pscustomobject]@{hostname=$hostname;port=$port;open=$open}
}

testport

hostname  port  open
--------  ----  ----
yahoo.com   80  True
7
js2010

これを使用して接続をテストできます-PowerShell Code Repository (author 'BSonPosh')から取得:

「Test-Portは、指定されたポートへのTCP接続を作成します。デフォルトでは、3秒のタイムアウトでポート135に接続します。 "

Param([string]$srv,$port=135,$timeout=3000,[switch]$verbose)

# Test-Port.ps1
# Does a TCP connection on specified port (135 by default)

$ErrorActionPreference = "SilentlyContinue"

# Create TCP Client
$tcpclient = new-Object system.Net.Sockets.TcpClient

# Tell TCP Client to connect to machine on Port
$iar = $tcpclient.BeginConnect($srv,$port,$null,$null)

# Set the wait time
$wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)

# Check to see if the connection is done
if(!$wait)
{
    # Close the connection and report timeout
    $tcpclient.Close()
    if($verbose){Write-Host "Connection Timeout"}
    Return $false
}
else
{
    # Close the connection and report the error if there is one
    $error.Clear()
    $tcpclient.EndConnect($iar) | out-Null
    if(!$?){if($verbose){write-Host $error[0]};$failed = $true}
    $tcpclient.Close()
}

# Return $true if connection Establish else $False
if($failed){return $false}else{return $true}

あなたはフォローアップのためにそのリポジトリページに行くことができます(この答えはすでにコピージョブでは多すぎます)

4
Jan Doggen

より迅速な方法は次のとおりです。

param($ip,$port)
New-Object System.Net.Sockets.TCPClient -ArgumentList $ip, $port

結果は次のようになります:

Client              : System.Net.Sockets.Socket
Available           : 0
Connected           : True
ExclusiveAddressUse : False
ReceiveBufferSize   : 65536
SendBufferSize      : 65536
ReceiveTimeout      : 0
SendTimeout         : 0
LingerState         : System.Net.Sockets.LingerOption
NoDelay             : False

興味深い価値は「つながる」

編集:もう1つの理由:Test-NetConnectionはPowershell v5からのみ機能します(私が正しく覚えている場合)が、このソリューションはv2から機能します:)

2
laurent

@Janの答えを取り入れることで、煩雑さが減り、生成されたタスクで機能するようになりました。
例外をスローし、$error /非標準の「冗長」なものを使用するAPIユーザーにも依存しないのはいいことです(.Connectedを取得すると、SocketExceptionが生成されるようですきちんとした)。

        Function TestTCP { Param($address, $port, $timeout=2000)
            $socket=New-Object System.Net.Sockets.TcpClient
            try {
                $result=$socket.BeginConnect($address, $port, $NULL, $NULL)
                if (!$result.AsyncWaitHandle.WaitOne($timeout, $False)) {
                    throw [System.Exception]::new('Connection Timeout')
                }
                $socket.EndConnect($result) | Out-Null
                $socket.Connected
            }
            finally {
                $socket.Close()
            }
        }
0
Hashbrown

私は多くのIPにpingを送信する超高速の方法を探していて、この質問に遭遇しました(とりわけ)。

やがて、自分のやりたいことに簡単に統合できるスクリプトを見つけました。男はそれを Fast Ping Sweep Asynchronous と呼んでいます。

Power Shell n00bであっても、出力をパイプ処理して、出力を変更して必要なものだけを含めることができました。他のスクリプトに出くわしましたが、自分の目的に合わせて変更するスクリプトを解読できませんでした。

これに必要なPower Shellのバージョンはわかりませんが、v4およびv5で動作します。

Powershell IPスキャナー、pingスイープスクリプトのほとんどを見てきましたが、いずれもPingASyncメソッドを使用していません。同期スクリプトの「問題」は、次のアドレスに進む前にノードが応答またはタイムアウトするまで待機する必要があることです。このアプローチには数秒かかる場合があります

function Global:Ping-IPRange {
    <#
    .SYNOPSIS
        Sends ICMP echo request packets to a range of IPv4 addresses between two given addresses.

    .DESCRIPTION
        This function lets you sends ICMP echo request packets ("pings") to 
        a range of IPv4 addresses using an asynchronous method.

        Therefore this technique is very fast but comes with a warning.
        Ping sweeping a large subnet or network with many swithes may result in 
        a peak of broadcast traffic.
        Use the -Interval parameter to adjust the time between each ping request.
        For example, an interval of 60 milliseconds is suitable for wireless networks.
        The RawOutput parameter switches the output to an unformated
        [System.Net.NetworkInformation.PingReply[]].

    .INPUTS
        None
        You cannot pipe input to this funcion.

    .OUTPUTS
        The function only returns output from successful pings.

        Type: System.Net.NetworkInformation.PingReply

        The RawOutput parameter switches the output to an unformated
        [System.Net.NetworkInformation.PingReply[]].

    .NOTES
        Author  : G.A.F.F. Jakobs
        Created : August 30, 2014
        Version : 6

    .EXAMPLE
        Ping-IPRange -StartAddress 192.168.1.1 -EndAddress 192.168.1.254 -Interval 20

        IPAddress                                 Bytes                     Ttl           ResponseTime
        ---------                                 -----                     ---           ------------
        192.168.1.41                                 32                      64                    371
        192.168.1.57                                 32                     128                      0
        192.168.1.64                                 32                     128                      1
        192.168.1.63                                 32                      64                     88
        192.168.1.254                                32                      64                      0

        In this example all the ip addresses between 192.168.1.1 and 192.168.1.254 are pinged using 
        a 20 millisecond interval between each request.
        All the addresses that reply the ping request are listed.

    .LINK
        http://gallery.technet.Microsoft.com/Fast-asynchronous-ping-IP-d0a5cf0e

    #>
    [CmdletBinding(ConfirmImpact='Low')]
    Param(
        [parameter(Mandatory = $true, Position = 0)]
        [System.Net.IPAddress]$StartAddress,
        [parameter(Mandatory = $true, Position = 1)]
        [System.Net.IPAddress]$EndAddress,
        [int]$Interval = 30,
        [Switch]$RawOutput = $false
    )

    $timeout = 2000

    function New-Range ($start, $end) {

        [byte[]]$BySt = $start.GetAddressBytes()
        [Array]::Reverse($BySt)
        [byte[]]$ByEn = $end.GetAddressBytes()
        [Array]::Reverse($ByEn)
        $i1 = [System.BitConverter]::ToUInt32($BySt,0)
        $i2 = [System.BitConverter]::ToUInt32($ByEn,0)
        for($x = $i1;$x -le $i2;$x++){
            $ip = ([System.Net.IPAddress]$x).GetAddressBytes()
            [Array]::Reverse($ip)
            [System.Net.IPAddress]::Parse($($ip -join '.'))
        }
    }

    $IPrange = New-Range $StartAddress $EndAddress

    $IpTotal = $IPrange.Count

    Get-Event -SourceIdentifier "ID-Ping*" | Remove-Event
    Get-EventSubscriber -SourceIdentifier "ID-Ping*" | Unregister-Event

    $IPrange | foreach{

        [string]$VarName = "Ping_" + $_.Address

        New-Variable -Name $VarName -Value (New-Object System.Net.NetworkInformation.Ping)

        Register-ObjectEvent -InputObject (Get-Variable $VarName -ValueOnly) -EventName PingCompleted -SourceIdentifier "ID-$VarName"

        (Get-Variable $VarName -ValueOnly).SendAsync($_,$timeout,$VarName)

        Remove-Variable $VarName

        try{

            $pending = (Get-Event -SourceIdentifier "ID-Ping*").Count

        }catch [System.InvalidOperationException]{}

        $index = [array]::indexof($IPrange,$_)

        Write-Progress -Activity "Sending ping to" -Id 1 -status $_.IPAddressToString -PercentComplete (($index / $IpTotal)  * 100)

        Write-Progress -Activity "ICMP requests pending" -Id 2 -ParentId 1 -Status ($index - $pending) -PercentComplete (($index - $pending)/$IpTotal * 100)

        Start-Sleep -Milliseconds $Interval
    }

    Write-Progress -Activity "Done sending ping requests" -Id 1 -Status 'Waiting' -PercentComplete 100 

    While($pending -lt $IpTotal){

        Wait-Event -SourceIdentifier "ID-Ping*" | Out-Null

        Start-Sleep -Milliseconds 10

        $pending = (Get-Event -SourceIdentifier "ID-Ping*").Count

        Write-Progress -Activity "ICMP requests pending" -Id 2 -ParentId 1 -Status ($IpTotal - $pending) -PercentComplete (($IpTotal - $pending)/$IpTotal * 100)
    }

    if($RawOutput){

        $Reply = Get-Event -SourceIdentifier "ID-Ping*" | ForEach { 
            If($_.SourceEventArgs.Reply.Status -eq "Success"){
                $_.SourceEventArgs.Reply
            }
            Unregister-Event $_.SourceIdentifier
            Remove-Event $_.SourceIdentifier
        }

    }else{

        $Reply = Get-Event -SourceIdentifier "ID-Ping*" | ForEach { 
            If($_.SourceEventArgs.Reply.Status -eq "Success"){
                $_.SourceEventArgs.Reply | select @{
                      Name="IPAddress"   ; Expression={$_.Address}},
                    @{Name="Bytes"       ; Expression={$_.Buffer.Length}},
                    @{Name="Ttl"         ; Expression={$_.Options.Ttl}},
                    @{Name="ResponseTime"; Expression={$_.RoundtripTime}}
            }
            Unregister-Event $_.SourceIdentifier
            Remove-Event $_.SourceIdentifier
        }
    }
    if($Reply -eq $Null){
        Write-Verbose "Ping-IPrange : No ip address responded" -Verbose
    }

    return $Reply
}