Windows批处理脚本:如何检查一系列服务器/端口是否开放?

3

我可以运行一个命令来查看端口是否打开:

portqry -n servername -e 80

我想要对服务器列表进行测试:
  1. DEV1 80
  2. DEV2 80
  3. DEV3 80
  4. TEST1 80
  5. TEST2 80
  6. PROD 80
我希望使用一个单一的Windows批处理脚本来测试它们,看哪些端口是打开的,哪些端口是关闭的。(而且我不希望它在第一个关闭的端口上停止)。
我的问题是:是否有一个Windows批处理脚本可以查看一系列的服务器/端口是否已经打开? 另外:我知道有其他问题询问如何检查端口是否已经打开。但它们中没有一个关于以可靠方式为端口序列编写脚本的问题。

对于这样一个广泛的、与编程无关的问题,一个好的回答只会是:“是的,有。”因为我知道你知道这不是一个代码编写服务。 - SomethingDark
对于每个服务器,for %%I in (dev1 dev2 dev3 test1 test2 prod) do portqry -n %%I -e 80 可以让您循环遍历。或者,如果您不仅想检查端口是否打开,还想检查Web服务是否适当地提供内容,此猫的问题中的脚本可以很容易地根据您的需求进行调整。 - rojo
@hawkeye - 这不应该发生。Stack Overflow 不是一个代码编写服务。寻求帮助来完成两行批处理脚本没问题。但请求别人为你编写脚本则不行。 - SomethingDark
@SomethingDark 你说得没错。然而,在我看来,一个为这个网站做出了如此多贡献的人值得偶尔得到一些纵容。此外,为他的问题提供解决方案可能会帮助未来的其他人。 - rojo
@rojo - 没错。 - SomethingDark
显示剩余3条评论
3个回答

8

这就是你需要的信息。继续传递。 :) 直接回答你的问题,只需使用一个 for 循环遍历你的服务器并对每个服务器执行 portqry编辑: 你找到的 PowerShell 片段有助于摆脱 PortQry 的依赖。

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        call :handshake "%%~a" "!port!" && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

goto :EOF

:handshake <server> <port>
powershell "$t=new-object Net.Sockets.TcpClient;$c=$t.BeginConnect('%~1',%~2,{},{});if($c.AsyncWaitHandle.WaitOne(1000)){$t.EndConnect($c);exit 0};exit 1"
exit /b %ERRORLEVEL%

以下是使用PortQry 2.0的原始解决方案:

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        portqry -n "%%~a" -e "!port!" >NUL 2>NUL && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

如果你只是测试Web服务,那么用不同的方法可能更加合适。你可以使用Microsoft.XMLHTTP COM对象来摆脱portqry依赖,并且通过这种方式获取的响应将与HTTP服务更相关。例如,如果你在端口8080上运行VNC服务器,而你期望监听Web服务,则portqry可能会返回成功,但你需要它返回失败。无论如何,将此保存为.bat脚本并根据需要进行修改。
@if (@CodeSection == @Batch) @then

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        cscript /nologo /e:JScript "%~f0" "%%~a" "!port!" && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

goto :EOF

@end // end batch / begin JScript chimera

var server = WSH.Arguments(0),
    port = WSH.Arguments(1),
    protocol = port == 443 ? 'https' : 'http',
    URL = protocol + '://' + server + ':' + port + '/',
    XHR = WSH.CreateObject('Microsoft.XMLHTTP');

XHR.open('GET', URL);
XHR.setRequestHeader('User-Agent','XMLHTTP/1.0');
XHR.send('');
while (XHR.readyState != 4) WSH.Sleep(25);
WSH.Quit(XHR.status - 200);

我非常喜欢你的回答 - 但它假设我正在联系一个HTTP服务器,而实际上我只想知道端口是否开放。需要进行哪些修改才能实现这一点? - hawkeye
尝试我的编辑中的第一个脚本。我留下了第二个,以防将来有人用得上,因为如果我自己说的话,它相当聪明。 - rojo
好的 - PowerShell 真是太棒了。 - hawkeye
@npocmaka并不是所有的Web服务器都运行在80和443端口上,有些用户可能希望通过代理测试Web服务器是否响应。 耸肩 - rojo
嗨Rojo,你使用Portquery的脚本运行得非常好。但它只输出OK/Error,我还需要知道端口是“Listening”还是“Not listening”。你能帮我吗? - Kiran Maroju
显示剩余4条评论

1

我最终用一个名为 porttest.ps1 的文件替换了上面的VBScript,使用 powershell -f porttest.ps1 命令来运行该文件。

param(
    [string] $remoteHost = "arbitrary-remote-hostname",
    [int] $port = 23
     )

# Open the socket, and connect to the computer on the specified port
write-host "Connecting to $remoteHost on port $port"
$tcpobject = new-Object system.Net.Sockets.TcpClient 
$connect = $tcpobject.BeginConnect($remoteHost,$port,$null,$null) 
#Configure a timeout before quitting - time in milliseconds 
$wait = $connect.AsyncWaitHandle.WaitOne(1000,$false) 
If (-Not $Wait) {
    'Timeout'
    exit 1
} Else {
    $error.clear()
    $tcpobject.EndConnect($connect) | out-Null 
        If ($Error[0]) {
            Write-warning ("{0}" -f $error[0].Exception.Message)
            exit 1
        } Else {
            'Port open!'
            exit 0
        }
    }

另一个肯定更好 - 但只是想贡献一些其他的想法,以保持思路活跃。 - hawkeye
1
使用PowerShell处理Net.Sockets.TcpClient是个好主意。如果您感兴趣,我已经将其纳入了我的答案。 - rojo

0

与上述不同的是,这不需要使用PowerShell,并且应该适用于所有已安装Telnet客户端的Windows版本(dism /online /Enable-Feature /FeatureName:TelnetClient)。

如果有人能够找出如何停止telnet命令输出任何内容,请添加评论,因为如果我添加重定向,它将拒绝运行。我相信telnet需要一个stdout并且不能被重定向。

要使此操作正常工作,只需将两个127.0.0.1 IP地址更改为您想要扫描的主机。

break>Results.txt & (for /l %a in (1,1,49152) do @taskkill /im telnet.exe /f >NUL 2>&1 & @start /b telnet.exe 127.0.0.1 %a & for /f %i in ('netstat -n ^| findstr /r /c:"TCP[ ][ ]*127.0.0.1:%a[ ][ ]*.*ESTABLISHED" ^| findstr /n ".*" ^| findstr "^1:"') do @echo "TCP Port Open: %a" >> Results.txt) & taskkill /im telnet.exe /f >NUL 2>&1

一旦完成,您可以输入以下内容来获取结果:

type Results.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接