轉了無數次。繼續轉。 就是關於TCP的偵活

來源:互聯網
上載者:User

目前手頭有個關於心博功能的一個案例, 在使用SOL_SOCKET, SO_KEEPALIVE上有一點心得,想寫出來和大家分享一下。

關於SOL_SOCKET選項SO_KEEPALIVE有一個很詳細的英文How TO, 在下面的網頁中大家可以看到詳細的內容
http://www.icewalkers.com/Linux/Howto/TCP-Keepalive-HOWTO/index.html

在《UNIX網路編程第1卷》中也有詳細的闡述:

SO_KEEPALIVE 保持串連檢測對方主機是否崩潰,避免(伺服器)永遠阻塞於TCP串連的輸入。設定該選項後,如果2小時內在此套介面的任一方向都沒有資料交換,TCP就自動給對方 發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會導致以下三種情況:對方接收一切正常:以期望的ACK響應。2小時後,TCP將發出另一個探測分節。對方已崩潰且已重新啟動:以RST響應。套介面的待處理錯誤被置為ECONNRESET,套介面本身則被關閉。對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖得到一個響應。在發出第一個探測分節11分鐘
15秒後若仍無響應就放棄。套介面的待處理錯誤被置為ETIMEOUT,套介面本身則被關閉。如ICMP錯誤是“host unreachable(主機不可達)”,說明對方主機並沒有崩潰,但是不可達,這種情況下待處理錯誤被置為 EHOSTUNREACH。

在該書的第158頁有更詳細的描述。

根據上面的介紹我們可以知道對端以一種非優雅的方式中斷連線的時候,我們可以設定SO_KEEPALIVE屬性使得我們在2小時以後發現對方的TCP串連是否依然存在。

keepAlive = 1;
Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

如果我們不能接受如此之長的等待時間,從TCP-Keepalive-HOWTO上可以知道一共有兩種方式可以設定,一種是修改核心關於網路方面的配置參數,另外一種就是SOL_TCP欄位的TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT三個選項。

The tcp_keepidle parameter specifies the interval of inactivity that causes TCP to generate a KEEPALIVE transmission for an application that requests them. tcp_keepidle defaults to 14400 (two hours).

/*開始首次KeepAlive探測前的TCP空閉時間 */

 

The tcp_keepintvl parameter specifies the interval between the nine retries that are attempted if a KEEPALIVE transmission is not acknowledged. tcp_keepintvl defaults to 150 (75 seconds).
/* 兩次KeepAlive探測間的時間間隔  */

The TCP_KEEPCNT option specifies the maximum number of keepalive probes to be sent. The value of TCP_KEEPCNT is an integer value between 1 and n, where n is the value of the systemwide tcp_keepcnt parameter.

/* 判定斷開前的KeepAlive探測次數 */

因此我們可以得到
    int                 keepIdle = 6;
    int                 keepInterval = 5;
    int                 keepCount = 3;

    Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));

    Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));

    Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

我們需要注意的TCP-Keepalive-HOWTO上這段話:

Remember that keepalive is not program−related, but socket−related, so if you have multiple sockets, you can handle keepalive for each of them separately.

這些屬性是sockt繼承的,非整個代碼內的所有sockets都繼承這個屬性,因為如果要應用到多個套介面上必須分別使用Setsockopt, Setsockopt是setsockopt的包裹函數。

如果心搏函數要維護用戶端的存活,即伺服器必須每隔一段時間必須向客戶段發送一定的資料,那麼使用SO_KEEPALIVE是有很大的不足的。因為 SO_KEEPALIVE選項指"此套介面的任一方向都沒有資料交換",我不知道大家是怎麼理解這個實現的。在Linux 2.6系列上,上面話的理解是只要開啟SO_KEEPALIVE選項的套介面端檢測到資料發送或者資料接受就認為是資料交換。

因此在這種情況下使用 SO_KEEPALIVE選項 檢測對方是否非正常串連是完全沒有作用的,在每隔一段時間發包的情況, keep-alive的包是不可能被發送的。上層程式在非正常端開的情況下是可以正常發送包到緩衝區的。非正常端開的情況是指伺服器沒有收到"FIN" 或者 "RST"包。

當然這種情況也是比較好斷定對方是否存活,我提出來的主要原因是想看看大家對"此套介面的任一方向都沒有資料交換"是怎麼去理解的。

 

 

 

--------------------------

windows平台上也是差不多:

windows中也是可以做出系統層級的調整的, 對於Win2K/XP/2003,可以從下面的登錄機碼找到影響整個系統所有串連的參數:
        [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
        “KeepAliveTime”=dword:006ddd00
        “KeepAliveInterval”=dword:000003e8
        “MaxDataRetries”=”5″
在程式中來設定的話,首先先開啟keep-alive,跟在linux中是一樣的,
        BOOL bKeepAlive = TRUE;
        int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));

然後調整具體的參數,需要調用WSAIoctl
        tcp_keepalive alive_in = {0};
        tcp_keepalive alive_out = {0};
        alive_in.keepalivetime = 5000;
        alive_in.keepaliveinterval = 1000;
        alive_in.onoff = TRUE;
        unsigned long ulBytesReturn = 0;
        nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);

開啟Keepalive選項之後,對於使用IOCP模型的伺服器端程式來說,一旦檢測到串連斷開,GetQueuedCompletionStatus函數將立即返回FALSE,使得伺服器端能及時清除該串連、釋放該串連相關的資源。對於使用 select模型的用戶端來說,串連斷開被探測到時,以recv目的阻塞在socket上的select方法將立即返回SOCKET_ERROR,從而得知串連已失效,用戶端程式便有機會及時執行清除工作、提醒使用者或重新串連。

 

 

-------------------------------------------------------

還要附上另外一個文章:

由於平日需經常編輯 NAS server 的 /etc/hosts 檔, 以管控所有工作站電腦連接 NFS 的權限, 然而現在的需求是, 針對 hosts 包含的主機位址, 製作一個即時網路監控的頁面, 並於每次更動 hosts 檔, 監控頁面需同時更新. 這次用到 shell 及 Perl 兩種語言來完成所有工作, 分別有

1) host_list.sh

<coolcode lang="actionscript"> #!/bin/sh
PATH=/bin:/usr/bin
workdir="/root/bin"
rhost="nas.mydomain.com"
rlname="myuser"
tmp1_file="/tmp/hosts.nas.tmp"

cd $workdir
## copy file from remote host
rcp $rlname@$rhost:/etc/hosts $tmp1_file

## filter the string we don't need
linenum=$( cat -n $tmp1_file | grep "# For producing" | awk '{print $1}' )
sed -n "$linenum,$p" $tmp1_file | grep -v "#"</coolcode>

PS. 由於在作者的環境, 監控主機與被監控主機不同, 所以利用 rshell 從遠端主機 nas.mydomain.com 複製 /etc/hosts 到本機端, 然後在過濾掉 # 開頭的行列.
linenum 是配合筆者公司的特殊需求所制定的; 這指令只是將列表內容過濾掉我們不需要的資訊, 之後在由第二個指令執行.

2) host_ping.pl

<coolcode lang="perl"> #! /usr/bin/perl -w

use Net::Ping;

$hostexec = "/root/bin/host_list.sh";
$ping_log = "/lvm/webroot/mon.server/cgi-bin/ping.log";

open(HOSTS, "$hostexec|") or die "Unable to execute $hostexec:$! ";
open(FHD, "> $ping_log") or die "$! ";
my $p = Net::Ping->new('icmp');
print "Please wait 3 minutes… ";
while (<HOSTS>){
($ip,$hostname) = split(' ');
my $result = $p->ping($ip, 2);
my $now = get_time();
print FHD "$hostname,$ip,$result,$now ";

}
close(FHD);
close(HOSTS);

sub get_time {
my ($sec,$min,$hour,$day,$mon,$year)=localtime(time);
$mon++;
if ( length ($mon) == 1 ) {$mon = '0'.$mon;}
if ( length ($day) == 1 ) {$day = '0'.$day;}
if ( length ($hour) == 1 ) {$hour = '0'.$hour;}
if ( length ($min) == 1 ) {$min = '0'.$min;}
if ( length ($sec) == 1 ) {$sec = '0'.$sec;}
$year+=1900;
my $alltime="$year/$mon/$day $hour:$min:$sec";
}
</coolcode>

PS. 這指令將 host_list.sh 輸出的所有主機位址, 一一執行ping 並紀錄結果於 ping.log .

3) prodhosts.pl, 這是網頁檔, 請將它放在可執行 cgi 的網頁目錄內.

<coolcode lang="perl"> #! /usr/bin/perl

my $ping_log = "/lvm/webroot/mon.server/cgi-bin/ping.log";

print "Content-type: text/html ";
print <<HTML1;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Tester Workstation – Network Status</title>
<meta http-equiv="Refresh" content="300">
<meta http-equiv="Pragma" content="no-cache">
<STYLE>
<!–
tr { background-color: #B5E69D}
.normal { background-color: #B5E69D }
.highlight { background-color: #F8AB7C }

//–>
</style>

</head>

<body bgproperties="fixed" background="/images/bg2.gif">
<p align="center"><b style="color: rgb(51, 51, 255);">Tester Workstation(UNIX&Linux) – Network Status</b><br>
</p>
<center>
<table style="border-collapse: collapse;" border="1" bordercolor="#000000" cellpadding="5" cellspacing="0" width="70%">
<tbody>
<tr bgcolor="#d7d1cc">
<td align="center" width="25%">Host IP</td>
<td align="center" width="25%">Host Name</td>
<td align="center" width="15%">Host Status</td>
<td align="center" width="35%">Check Time</td>
</tr>
HTML1

open(FHD, "$ping_log") or die "$! ";
while (<FHD>) {
chomp;
my ($hostname, $ip, $stats, $cktime) = split(/,/);
print "<tr onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'"> ";
print "<td align="center" width="25%">$ip</td> ";
print "<td align="center" width="25%">$hostname</td> ";
print "<td align="center" width="15%">";
if ($stats) {
print "<img src="/images/server_up.gif" title="host is up">";
} else {
print "<img src="/images/server_down.gif" title="host is down">";
}
print "</td> ";
print "<td align="center" width="35%">$cktime</td> ";
print "</tr> ";
}
close(FHD);

print <<HTML2;
</tbody>
</table>
</center>
<p align="center"><u>
This page designed and maintained by A-Lang of MIS Dept.<br>
</u></p>
</body>
</html>

HTML2
</coolcode>

PS. 請自行修改 $ping_log .

成果展示圖:

 

原作文章如下: 

26. 網路程式設計
本章介紹 Perl 網路程式設計。

26.1 偵測主機存活
這一節我們要來寫一支簡單的網路程式,用來偵測您所管理的主機是否存活。

程式分成二部份:

偵測程式

觀看程式,以 CGI 程式來呈現。

首先是偵測程式,這支程式需用到 Net::Ping 這個模組。

先來看一下,系統中是否已安裝 Net::Ping? 檢查的方法如下: 

<coolcode lang="perl"> #! /usr/bin/perl

use Net::Ping; </coolcode>

以上存成 ping.pl,chmod +x ping.pl,執行 ./ping.pl

若沒有出現任何錯誤訊息,則表示該模組已經有安裝了,否則,表示沒有安裝。

若出現錯誤訊息,則要先來裝一下 Net::Ping,安裝方法如下:

<coolcode lang="actionscript"> perl -MCPAN -e shell

cpan>install Net::Ping </coolcode>

請參考第 10 章模組的安裝說明。

26.2 偵測程式
接著,我們利用 Net::Ping 來寫一支簡單的偵測程式,如下:

<coolcode lang="perl"> #! /usr/bin/perl

use Net::Ping;
use strict;

# 網站主要目錄的路徑,請修改成您的現況
my $prefix="/home/apache/htdocs";

# 記錄檔路徑
my $ping_log = "$prefix/ping.log";
open(FHD, "> $ping_log") || die "$!
";

my $p = Net::Ping->new('icmp');

# 欲偵測的主機 IP 列表,這裡只是舉例,請把它改成您管理的主機 IP
my @HOST=qw(
10.1.1.1
10.1.1.2
10.1.1.3
10.1.1.4
10.1.1.222
);

my $i;
for ($i=0; $i<=$#HOST; $i++) {

# 只 ping 一秒鐘,超過一秒鐘沒有反應,即視為斷訊
# 若連通則 $result 值為 1,若斷訊 $result 值為 0
my $result=$p->ping($HOST[$i], 1);

# 取得時間
my $now=get_time();

# 寫入記錄檔 ping.log 中
print FHD "$HOST[$i],$result,$now
";
}

close(FHD);

# 取得時間的副程式
sub get_time {

# 取得秒, 分, 時, 日, 月, 年
my ($sec,$min,$hour,$day,$mon,$year)=localtime(time);

# 月比實際少一, 所以加 1
$mon++;

# 判斷是否為個位數, 若是則在前面補 0
if (length ($mon) == 1) {$mon = '0'.$mon;}
if (length ($day) == 1) {$day = '0'.$day;}
if (length ($hour) == 1) {$hour = '0'.$hour;}
if (length ($min) == 1) {$min = '0'.$min;}
if (length ($sec) == 1) {$sec = '0'.$sec;}

# 年比實際西元年少 1900, 所以加上 1900
$year+=1900;

# 組合成完整的時間
my $alltime="$year/$mon/$day $hour:$min:$sec";

} </coolcode>

使用法:(需要 root 權限才能執行)

1. 將上述程式存成 ping.pl,放入 /root 中

2. 給執行權:

   chmod +x ping.pl

3. 放入 crontab 中,每 5 分鐘定時執行一次:

   crontab -u root -e

   */5 * * * * /root/ping.pl 

26.3 觀看程式
寫一支簡易的 CGI 程式,以觀看偵測的結果,如下:

<coolcode lang="perl"> #! /usr/bin/perl

print "Content-type: text/html

";

print <<HERE;
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=big5">
<title>管理主機存活列表</title>
</head>
<body bgcolor="white">
<table border=2 align=center>
<tr><td colspan=3 align=center><H1>管理主機存活列表</H1></td></tr>
<tr><td align=center>主機</td><td align=center>連線狀況</td><td align=center>偵測時間</td></tr></tr>
HERE

# 網站主要目錄的路徑,請修改成您的現況
my $prefix="/home/apache/htdocs";

# 記錄檔路徑
my $ping_log = "$prefix/ping.log";
open(FHD, "$ping_log") || die "$!
";

while(<FHD>) {
chomp;
my ($host, $alive_or_not, $time)=split(/,/);
my $status=($alive_or_not) ? "<font color=blue>連 通</font>" : "<font color=red>斷 訊</font>";
print "<tr><td>$host</td><td align=center>$status</td><td align=center>$time</td></tr>
";
}

close(FHD);

print <<HERE2;
</table>
</body>
</html>

HERE2 </coolcode>

使用法:
1. 將上述程式存成 viewping.cgi,放入 Web 的 cgi-bin 目錄中

2. 給執行權:

chmod +x viewping.cgi

3. 在瀏覽器中執行:

http://您的主機/cgi-bin/viewping.cgi

以下是執行結果: 

轉載自

http://linux.tnc.edu.tw/techdoc/perl_intro/c1225.html 

 

 

雖然我不會shell和Perl

但是我覺得給我們一個思路就是ping是可以用的拉,就是其實真的還有很多種方法,只是這些層面可能更接近了鏈路那一層了。先發著。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.