我正在寻找一种快速而简单的方法,用于正确地测试一个给定的TCP端口是否在远程服务器上打开,从Shell脚本中。
我已经设法用telnet命令做到这一点,当端口被打开时,它工作得很好,但当它没有超时时,它似乎并没有超时,只是挂在那里……
下面是一个例子:
l_TELNET=`echo "quit" | telnet $SERVER $PORT | grep "Escape character is"`
if [ "$?" -ne 0 ]; then
echo "Connection to $SERVER on port $PORT failed"
exit 1
else
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
fi
我要么需要一个更好的方法,或者一种方法强制telnet超时,如果它没有在8秒内连接,例如,并返回一些我可以在Shell中捕获的东西(返回代码,或stdout中的字符串)。
我知道Perl方法,它使用IO::Socket::INET模块,并编写了一个成功的测试端口的脚本,但如果可能的话,宁愿避免使用Perl。
注意:这是我的服务器正在运行(我需要从哪里运行这个)
SunOS 5.10 Generic_139556-08 i86pc i386 i86pc
这在幕后使用telnet,在mac/linux上似乎工作得很好。它不使用netcat,因为linux/mac版本之间的差异,这适用于默认的mac安装。
例子:
$ is_port_open.sh 80 google.com
OPEN
$ is_port_open.sh 8080 google.com
CLOSED
is_port_open.sh
PORT=$1
HOST=$2
TIMEOUT_IN_SEC=${3:-1}
VALUE_IF_OPEN=${4:-"OPEN"}
VALUE_IF_CLOSED=${5:-"CLOSED"}
function eztern()
{
if [ "$1" == "$2" ]
then
echo $3
else
echo $4
fi
}
# cross platform timeout util to support mac mostly
# https://gist.github.com/jaytaylor/6527607
function eztimeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }
function testPort()
{
OPTS=""
# find out if port is open using telnet
# by saving telnet output to temporary file
# and looking for "Escape character" response
# from telnet
FILENAME="/tmp/__port_check_$(uuidgen)"
RESULT=$(eztimeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1)
rm -f $FILENAME;
SUCCESS=$(eztern "$RESULT" "Escape character is '^]'." "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")
echo "$SUCCESS"
}
testPort
我猜现在给答案已经太迟了,而且这个答案可能不太好,但是你可以……
把它放在一个while循环中,并在上面加一个计时器。我更喜欢Perl而不是Solaris,但是根据你所使用的shell,你应该能够做一些像这样的事情:
TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever
然后在while循环中添加一个标志,这样如果它在完成之前超时,你可以引用超时作为失败的原因。
我怀疑telnet也有一个超时开关,但就我所知,我认为上面的方法是可行的。
在Bash中,为TCP/UDP连接使用伪设备文件非常简单。以下是剧本:
#!/usr/bin/env bash
SERVER=example.com
PORT=80
</dev/tcp/$SERVER/$PORT
if [ "$?" -ne 0 ]; then
echo "Connection to $SERVER on port $PORT failed"
exit 1
else
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
fi
测试:
$ ./test.sh
Connection to example.com on port 80 succeeded
下面是一行代码(Bash语法):
</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.
请注意,有些服务器可以通过防火墙保护免受SYN flood攻击,因此您可能会遇到TCP连接超时(~75秒)。要解决超时问题,请尝试:
timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.
参见:如何减少TCP connect()系统调用超时?
如果您正在使用ksh或bash,它们都支持使用/dev/tcp/ ip / port构造将IO重定向到/从套接字。在这个Korn shell示例中,我将从套接字重定向no-op的(:)标准输入:
W$ python -m SimpleHTTPServer &
[1] 16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000
如果套接字没有打开,shell将打印一个错误:
W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]
因此,你可以在if条件中使用这个测试:
SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
print succeeded
else
print failed
fi
no-op是在一个子shell中,所以如果stin重定向失败,我可以扔掉std-err。
我经常使用/dev/tcp来检查HTTP上资源的可用性:
W$ print arghhh > grr.html
W$ python -m SimpleHTTPServer &
[1] 16863
Serving HTTP on 0.0.0.0 port 8000 ...
W$ (print -u9 'GET /grr.html HTTP/1.0\n';cat <&9) 9<>/dev/tcp/127.0.0.1/8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.6.1
Date: Thu, 14 Feb 2013 12:56:29 GMT
Content-type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Feb 2013 12:55:44 GMT
arghhh
W$
这一行代码打开文件描述符9,用于读取和写入套接字,将HTTP GET打印到套接字,并使用cat从套接字读取。