跳转至主要内容
您的应用程序报告无法连接到后端数据库服务器(IP地址为172.16.1.116),或者您正准备部署新应用程序,需要确认其能否访问后端数据库服务器。 首先想到的工具是ping,但许多主机已不再响应ICMP回显(ping)请求;即使响应,其网络防火墙也极可能阻断您的ping请求或其回复。若ping超时(图1),尚有其他方法可测试网络连通性。
ping 172.16.1.116
Pinging host 172.16.1.116 : 172.16.1.116
ping: No reply. Time Out !!
ping: No reply. Time Out !!
ping: No reply. Time Out !!
ping: No reply. Time Out !!
Host 172.16.1.116 replied to 0 of the 4 pings
ready 12:26:32
图1 – ping超时
假设您要连接的主机运行的是基于TCP(而非UDP)的服务,只需通过telnet连接该主机并指定服务端口号即可。可能出现四种响应:连接成功(图2)、连接拒绝(图6)、异常提示(图7和图8)或超时(图9)。
telnet 172.16.1.116 1830
Trying...
Connected to 172.16.1.116.
Escape character is '^]'.
图2 – 连接
“已连接”提示表明您已连接至主机——大概是这样。某些广域网加速器会拦截连接并作为代理完成连接。若您刚建立连接几秒后又断开,可能是您连接到了加速器,但加速器无法连接到最终目标,因此断开了与您的连接。 当然也可能是您连接到实际主机后应用程序崩溃所致。建议您确认是否使用了任何加速器硬件,并了解其具体工作原理。
要断开连接,请在 telnet> 提示符下输入 control-] 并输入“quit”。
telnet 172.16.1.116 1830
Trying...
Connected to 172.16.1.116.
Escape character is '^]'.
telnet> quit
图3 – 断开已连接的会话
要绝对确定已连接到目标主机上运行的正确服务,唯一的方法是该服务在响应连接请求时发送某种标识信息。例如,显示主机信息的登录提示。若您能访问目标主机且该主机安装了Perl,可运行tcplisten Perl脚本——该脚本在接受连接时会发送指定的标识信息。
On target system (Linux)
[ndav@phx-lab-lnx64 ~]$ perl tcplisten.pl -p 1830 -m 'Connection has been accepted'
perl tcplisten.pl -port 1830 -message 'Connection has been accepted'
On OpenVOS client
stp -ttp ascii
ready 12:16:34
telnet 164.152.77.155 1830
Trying...
Connected to 164.152.77.155.
Escape character is '^]'.
Connection has been accepted
Escape character is '^]'.Connection closed by forei.
Ready 12:16:40
图4 在Linux系统上运行tcplisten.pl脚本,配合OpenVOS客户端
由于OpenVOS Telnet客户端在关闭连接时会清除终端窗口,您可能需要将终端类型设置为ascii才能查看连接消息。
您也可以在 OpenVOS 上运行 tcplisten(前提是您已安装 gnu_library)。
On OpenVOS target system
perl tcplisten.pl -p 1830 -m 'made it to m16'
perl tcplisten.pl -port 1830 -message 'made it to m16'
On Linux client
[ndav@phx-lab-lnx64 ~]$ telnet 164.152.77.128 1830
Trying 164.152.77.128...
Connected to rigel.az.stratus.com (164.152.77.128).
Escape character is '^]'.
made it to m16
Connection closed by foreign host.
[ndav@phx-lab-lnx64 ~]$
图5 在OpenVOS系统上运行tcplisten.pl脚本,配合Linux客户端
“拒绝”消息表示您已连接到目标主机,但该主机很可能没有在该端口上监听服务。
telnet 172.16.1.116 1830
Trying...
telnet: Unable to connect to remote host: The connection was refused.
ready 12:31:28
图6 – 拒绝
某些防火墙可能会对未经批准的连接请求发送重置响应。至少从客户端角度来看,很难甚至不可能判断具体是哪种情况。
如果telnet接收到网络(图7)或主机(图8)不可达的指示,它将报告该情况。 “网络…不可达”的提示表明:要么OpenVOS系统未配置通往目标网络的路由,要么网络中某台路由器无法连接至目标网络。而“无路由至主机”的提示则表示:连接目标网络的路由器无法访问目标主机,这通常意味着目标主机已下线。
telnet 172.16.1.116 1830
Trying...
telnet: Unable to connect to remote host: Network trying to be reached is unreac
+hable.
ready 12:54:34
图7 – OpenVOS Telnet客户端报告网络不可达。
telnet 172.16.1.116 1830
Trying...
telnet: Unable to connect to remote host: No route to host.
ready 12:55:44
图8 – OpenVOS Telnet客户端报告主机不可达
Telnet不会报告这些错误的来源。指向相同IP地址和端口的tuc.pl(测试UDP连接)Perl脚本将报告源IP地址(参见下文关于UDP测试的讨论)。
超时表示您可能无法访问该主机。大多数主机会响应连接请求或拒绝请求,但若主机启用了主机级防火墙,则可能直接丢弃请求而不作响应。同样,网络级防火墙也可能直接丢弃请求而不作响应;当然,路由器或主机发送的任何ICMP消息也可能被其他路由器或防火墙丢弃。
telnet 172.16.1.116 1830
Trying...
telnet: Unable to connect to remote host: The operation timed out.
图9 – OpenVOS telnet客户端报告超时
总而言之,共有5种可能的结果。
消息
意义
互联(带横幅)
可访问主机和应用程序
互联(无横幅)
可能能够访问主机和应用程序
被拒绝
能连接主机但无法访问应用程序——可能是
无法接通
无法连接主机
超时
无法连接主机——可能是
图10 – TCP连接结果与解释的总结
如果应用程序使用UDP端口,情况会更复杂。首先,OpenVOS系统上没有标准工具可用于发送UDP数据报。 其次,响应可能以UDP数据报或ICMP消息形式返回。tuc.pl(测试UDP连接)Perl脚本会向指定主机和端口发送包含当前日期时间的数据报,随后等待UDP数据报或ICMP消息响应。理想情况下,应用程序应返回可显示的消息。图11显示监听应用程序成功回显了脚本发送的消息。
perl tuc.pl -i 172.16.1.116 -p 1830
Reply received on UDP socket from 164.152.77.128 message: echoed by udpecho.pl:
+sent by tuc.pl at Mon Aug 30 13:31:34 2010
ready 13:31:34
图11 – UDP服务器应用程序回显接收到的消息
如果服务器未监听目标端口,则应返回“目标端口不可达”的错误信息。假设该错误信息来自目标IP地址,这意味着您已连接到服务器,但没有应用程序正在监听该端口。
perl tuc.pl -i 172.16.1.116 -p 1830
ICMP Destination port unreachable message received from 172.16.1.116
ready 11:40:12
图12 – 目标主机发送端口不可达消息
与telnet测试类似,您可获取指示网络及主机问题的ICMP消息。如前所述,您还能获取发送方的IP地址,从而获得更明确的调查起点。
perl tuc.pl -i 172.16.1.116 -p 1830
ICMP Destination network unreachable message received from 164.152.77.171
ready 11:43:50
perl tuc.pl -i 172.16.1.116 -p 1830
ICMP Destination host unreachable message received from 164.152.77.171
ready 11:44:54
图13 – 网络和主机不可达消息
上述错误可能是最常见的两种。另一种错误则表明存在路由环路。
perl tuc.pl -i 172.16.1.116 -p 1830
ICMP Time exceeded , TTL expired in transit message received from 164.152.77.34
ready 11:45:39
图14 – 指示路由环路的ICMP消息
还存在许多其他错误,其中部分错误表明管理员采取了阻止数据包传输的措施。这些消息很可能意味着防火墙正在阻挡您的数据包。
然而,许多应用程序期望接收特定格式的消息。在这种情况下,您很可能会遭遇沉默。默认情况下,脚本将在5秒后超时并报告“无响应”。
perl tuc.pl -i 172.16.1.116 -p 1830
No response was received from 172.16.1.116
ready 16:43:26
图15 – 无响应
若需延长(或缩短)等待时间,可使用“-t选项”。
perl tuc.pl -i 172.16.1.117 -p 1830 -t 10
No response was received from 172.16.1.117
ready 12:06:26
perl tuc.pl -i 172.16.1.117 -p 1830 -t 1
No response was received from 172.16.1.117
ready 12:06:32
图16 – 修改默认超时时间
沉默未必是坏事。这可能意味着你已连接到目标主机,目标端口正在监听,但它只是不喜欢收到的测试消息。 遗憾的是,这同样可能意味着测试数据包根本未送达目标主机,且任何返回的ICMP状态反馈均遭阻断。若您能访问该服务器,可运行udpecho.pl Perl脚本——该脚本会附加自身报头后回显所有接收到的数据(图11)。
总而言之
消息
意义
应用程序横幅消息
可访问主机和应用程序
ICMP网络/主机消息
无法连接主机
来自主机的ICMP端口消息
能连接到主机但无法访问应用程序
无响应,应用程序发送横幅消息
无法连接主机
无响应,应用程序未发送横幅消息
可能或可能无法连接到主机
图17 – UCP连接结果与解释的总结

tcplisten.pl

# tcplisten.pl begins here

#

# 版本 1.00 2010-07-30

# 此脚本已在以下环境测试通过:

# 运行在 i686-vos 架构上的 Open VOS 17.0.2ah 系统,搭载 Perl v5.8.0 版本

# 运行中的 Microsoft Windows XP Service Pack 3

# ActiveState Perl v5.10.0 专为 MSWin32-x86-multi-thread 构建

# 运行中的 Red Hat Linux 4AS-5.5

# Perl v5.8.5 构建于 x86_64-linux-thread-multi 架构

#

#stratus

#

use IO::Socket;

use Getopt::Long;

use Sys::Hostname;

use strict;

my ($result);

my ($localPort, $message, $peerAddr);

my ($newSock, $data);

$result = GetOptions('port=s' => $localPort,

'message=s' => $message);

如果 (($result != 1) || !(defined($localPort)))

{

打印“nn用法:n”;

print "tperl tcplisten.pl -port 端口号 [-message 消息]nn";

退出;

}

如果未定义 ($message)

{

打印“未指定消息——正在创建消息”;

$message = "连接已由在" . hostname . "上运行的 tcplisten 接受";

}

print "perl tcplisten.pl -port $localPort -message '" . $message . "'\n";

my $sock = IO::Socket::INET->new(

Proto => ‘tcp’

本地端口 => $localPort,

本地地址 => ‘0.0.0.0’

听 => 1,

) 或终止 "无法为端口 $localPort 创建套接字:$!n";

while (1)

{

my $newSock = $sock->accept();

打印 “已接受来自 ” . $newSock->peerhost .

在 ” . localtime() . “n”;

print $newSock $message . "\n";

$newSock -> 关闭(2);

$sock->read($data, 1024); # 等待收到关闭指示 */

$newSock->close();

}

#

# tcplisten.pl 到此结束

tuc.pl

# tuc.pl begins here

#

# 版本 1.00 2010-07-30

# 此脚本已在以下环境测试通过:

# 运行在 i686-vos 架构上的 Open VOS 17.0.2ah 系统,搭载 Perl v5.8.0 版本

# 运行中的 Microsoft Windows XP Service Pack 3

# ActiveState Perl v5.10.0 专为 MSWin32-x86-multi-thread 构建

# 运行中的 Red Hat Linux 4AS-5.5

# Perl v5.8.5 构建于 x86_64-linux-thread-multi 架构

#

#stratus

#

use IO::Socket;

use IO::Select;

use Getopt::Long;

use strict;

my ($result);

my ($目标IP, $目标端口, $超时时间, $结果, $消息, $标志);

my ($ready, $socket);

my ($x, $c, $icmpAlready);

$result = GetOptions('ip=s' => $destIP,

‘port=s’ => $destPort,

'timeout=s' => $timeout);

如果 (($result != 1) || !(定义了($destIP) 且 定义了($destPort)))

{

打印“nn用法:n”;

打印“tperl tuc.pl -ip 目标IP地址”

“-端口目标端口号nn”;

退出;

}

if (!defined($timeout)) { $timeout = 5; }

elsif ($timeout > 15) { print "OK,但 " . $timeout .

“似乎过长了。”; }

$message = “由 tuc.pl 在 ” . localtime() . " 发送";

my $sock = IO::Socket::INET->new(

Proto => ‘udp’

PeerPort => $destPort,

PeerAddr => $destIP

) 或死亡 “无法为目标 $destIP 创建套接字:$!n”;

my $isock = IO::Socket::INET->new(

Proto => 'icmp');

$sock->send($message) 或 die "发送错误: $!n";

$message = "";

my $read_set = new IO::Select();

$read_set->add($sock);

$read_set->add($isock);

($ready) = IO::Select->select($read_set, undef, undef, $timeout);

$icmpAlready = 0;

$c = 0;

foreach $socket (@$ready)

{

$c = $c + 1;

如果 ($socket == $sock)

{

$x = “从 UDP 套接字接收到的回复来自 “;

$socket->recv($message, 100, $flags);

如果 ($icmpAlready == 0)

{

如果 (消息长度 > 0)

{ print $x . $socket->peerhost . ” message: ” . $message . “n”; }

else { print $x . $socket->peerhost . “n”; }

}

}

else # 收到icmp消息

{

$icmpAlready = 1;

$socket->recv($message, 100, $flags);

如果 (消息长度 > 98)

{ print "意外地长(" . length($message) .

“来自” . $socket->peerhost . “的ICMP消息”

消息:". $message . "\n"; }

elsif (length ($message) < 52)

{ print "出乎意料地短(" . length($message) .

") ICMP消息来自 " . $socket->peerhost . "n"; }

else # 处理icmp消息

{

my $type = ord(substr($message, 20, 1));

my $code = ord(substr($message, 21, 1));

my $port = ord(substr($message, 50, 1)) * 256 +

ord(substr($message, 51, 1));

如果 ($port != $destPort)

{ print “意外收到来自 ” 的 ICMP 消息。

$socket->peerhost . " 消息:" . substr($message, 20) .

“n”; }

否则 # icmp 消息正常

{

如果 ($type == 3)

{

if ($code == 0) { print "ICMP 目标网络 " .

“收到无法送达的消息,来自 ”。

$socket->peerhost . "n"; }

elsif ($code == 1) { print "ICMP 目标主机 " .

“收到无法送达的消息,来自 ”。

$socket->peerhost . "n"; }

elsif ($code == 3) { print "ICMP 目标端口 " .

“收到无法送达的消息,来自 ”。

$socket->peerhost . "n"; }

elsif ($code == 6) { print "ICMP 目标网络 " .

“收到来自”的未知消息。

$socket->peerhost . "n"; }

elsif ($code == 7) { print "ICMP 目标主机未知 " .

“收到来自 ” . $socket->peerhost . “ 的消息”; }

elsif ($code == 8) { print "ICMP 源主机被隔离" .

“来自 的消息已收到”。

$socket->peerhost . "n"; }

elsif ($code == 9) { print "ICMP 网络管理层级 " .

“收到来自的禁止消息”。

$socket->peerhost . "n"; }

elsif ($code == 10) { print "ICMP 主机管理上已禁用" .

“收到来自的禁止消息”。

$socket->peerhost . "n"; }

elsif ($code == 11) { print “ICMP 网络不可达,目标为 ” .

“收到来自的TOS消息”。

$socket->peerhost . "n"; }

elsif ($code == 12) { print "ICMP 主机不可达,地址为 " .

“收到来自的TOS消息”。

$socket->peerhost . "n"; }

elsif ($code == 13) { print "ICMP 通信 " .

“收到来自的行政禁止消息”。

$socket->peerhost . "n"; }

else { print "收到意外的ICMP消息,类型为: " .

$type . “,代码 = ” . $code . ” 来自 ” .

$socket->peerhost . "n"; }

} # 如果 ($type == 3)

elsif (($type == 11) & ($code == 0))

{ 打印“ICMP超时,传输过程中TTL已过期”。

“收到来自 ” . $socket->peerhost . “ 的消息”; }

else { print "收到意外的ICMP消息,类型为: " .

$type . “,代码 = ” . $code . ” 来自 ” .

$socket->peerhost . "n"; }

} # 否则 # icmp 消息正常

} # 否则 # 处理icmp消息

} # 否则 #收到icmp消息

} # 遍历所有已就绪套接字 $socket (@$ready)

if ($c == 0) { print “未收到来自 ” .

$sock->peerhost . "n"; }

#

# tuc.pl 结束于此

udpecho.pl

# udpecho.pl begins here

#

# 版本 1.00 2010-07-30

# 此脚本已在以下环境测试通过:

# 运行在 i686-vos 架构上的 Open VOS 17.0.2ah 系统,搭载 Perl v5.8.0 版本

# 运行中的 Microsoft Windows XP Service Pack 3

# ActiveState Perl v5.10.0 专为 MSWin32-x86-multi-thread 构建

# 运行中的 Red Hat Linux 4AS-5.5

# Perl v5.8.5 构建于 x86_64-linux-thread-multi 架构

#

#stratus

#

use IO::Socket;

use IO::Select;

use Getopt::Long;

use strict;

my ($result);

my ($localPort, $debug, $result, $message, $flags);

my ($ready, $socket);

my ($x, $c);

$result = GetOptions('port=s' => $localPort,

‘debug’ => $debug);

如果 (($result != 1) || !(defined($localPort)))

{

打印“nn用法:n”;

print "tperl udpecho.pl -port 本地端口号 [-debug]nn";

退出;

}

if (defined($debug)) { print “local port number is ” . $localPort . “n”; }

my $sock = IO::Socket::INET->new(

Proto => ‘udp’

本地端口 => $localPort

) 或 死亡 “无法创建套接字 $!n”;

my $read_set = new IO::Select();

$read_set->add($sock);

$message = "";

while (1)

{

($ready) = IO::Select->select($read_set, undef, undef, 300);

foreach $socket (@$ready)

{

$socket->recv($message, 100, $flags);

if (defined ($debug)) { print "来自 " . $socket->peerhost . " 的消息" .

消息:". $message . "\n"; }

$socket->send("由 udpecho.pl 发送:" . $message) or die("发送错误:$!");

+n";

}

}

#

# udpecho.pl 到此结束