以前写过一篇文章,用来统计tcp连接总数,这周发现有时候也需要统计详细的tcp套接字信息,以便分析具体问题。
今天用awk写了个小脚本,用来统计各种tcp状态的详细信息,之所以不用netstat工具,是因为此君实在是慢,在笔者一台总连接数只有5147的机器上,netstat统计出来用了11秒,我写的脚本用了2.7秒,特别是在大负载的服务器环境,随着连接数的提高,这种效率的提高是必须考虑的,就是一种工作态度吧,不能只把事情做出来,要考虑怎么样做才是最好的方式,节省服务器的资源,可以让我们sa的工作更加轻松,ok,先看看时间比对:
纯粹的netstat:
[root@a work]# time netstat -an 2>/dev/null > c
real 0m11.130s
user 0m0.156s
sys 0m10.929s
偶写的awk脚本:
[root@a work]# time sh netstat-detail.sh > b
real 0m2.725s
user 0m0.288s
sys 0m2.416s
这个脚本中最费时的是这一步:
[root@a work]# time cat /proc/net/tcp /proc/net/tcp6 2>/dev/null > a
real 0m2.619s
user 0m0.004s
sys 0m2.592s
也就是说,如果能突破从proc下快速取文件的瓶颈,这个速度肯定能飞升,可是偶还深入不了这么深的深度,暂时只能做到这样啦。
下面是详细的脚本代码,希望能给各位从事sa的同仁们所用
#! /bin/sh
cat /proc/net/tcp /proc/net/tcp6 2>/dev/null > a
awk '{print $2,$3,$4}' /tmp/a | awk '
BEGIN { # set fs
FS = "[ ]*|:" ;
# print header
printf("Local-Address Foreign-Address State\n"); }
# drop trash message from file tcp && tcp6
( $0 !~ /local_address/ ){
# get ipv4addr from file /proc/net/tcp
if (length($1) == 8)
{
local_addr_ip4 = strtonum("0x"substr($1,1,2)) ;
local_addr_ip3 = strtonum("0x"substr($1,3,2)) ;
local_addr_ip2 = strtonum("0x"substr($1,5,2)) ;
local_addr_ip1 = strtonum("0x"substr($1,7,2)) ;
rem_addr_ip4 = strtonum("0x"substr($3,1,2)) ;
rem_addr_ip3 = strtonum("0x"substr($3,3,2)) ;
rem_addr_ip2 = strtonum("0x"substr($3,5,2)) ;
rem_addr_ip1 = strtonum("0x"substr($3,7,2)) ;
}
else
# get ipv6addr from file /proc/net/tcp6
{
local_addr_ip4 = strtonum("0x"substr($1,25,2)) ;
local_addr_ip3 = strtonum("0x"substr($1,27,2)) ;
local_addr_ip2 = strtonum("0x"substr($1,29,2)) ;
local_addr_ip1 = strtonum("0x"substr($1,31,2)) ;
rem_addr_ip4 = strtonum("0x"substr($3,25,2)) ;
rem_addr_ip3 = strtonum("0x"substr($3,27,2)) ;
rem_addr_ip2 = strtonum("0x"substr($3,29,2)) ;
rem_addr_ip1 = strtonum("0x"substr($3,31,2)) ;
}
local_port = strtonum("0x"$2) ;
rem_port = strtonum("0x"$4) ;
# analyze $5 tcp stat
if ( $5 ~ /06/ ) tcp_stat = "TIME_WAIT"
else if ( $5 ~ /02/ ) tcp_stat = "SYN_SENT"
else if ( $5 ~ /03/ ) tcp_stat = "SYN_RECV"
else if ( $5 ~ /04/ ) tcp_stat = "FIN_WAIT1"
else if ( $5 ~ /05/ ) tcp_stat = "FIN_WAIT2"
else if ( $5 ~ /01/ ) tcp_stat = "ESTABLISHED" ;
else if ( $5 ~ /07/ ) tcp_stat = "CLOSE"
else if ( $5 ~ /08/ ) tcp_stat = "CLOSE_WAIT"
else if ( $5 ~ /09/ ) tcp_stat = "LAST_ACK"
else if ( $5 ~ /0A/ ) tcp_stat = "LISTEN"
else if ( $5 ~ /0B/ ) tcp_stat = "CLOSING"
else if ( $5 ~ /0C/ ) tcp_stat = "MAX_STATES"
printf("%d.%d.%d.%d:%d %d.%d.%d.%d:%d %s\n",local_addr_ip1,local_addr_ip2,local_addr_ip3,local_addr_ip4,local_port,rem_addr_ip1,rem_addr_ip2,rem_addr_ip3,rem_addr_ip4,rem_port,tcp_stat);
}' | column -t
# experience : first column of tcp && tcp6 is not settled ! may " 1" may "134562" so it is difficult to set the very fs !!
之所以加上最后的经验说明,是因为刚开始的时候只想着一次取a的内容,分析完毕,可是后面发现tcp中第一列的内容不怎么规则,有的位数多了,超过1000,就没有空格了,这个给设置fs造成了非常大的麻烦,最后我才不得以直接用管道取出a的2345列。
附上此脚本的输出示例吧(服务和端口信息都是修改过的,以免泄露机密^_^):
[root@localhost tmp]# sh netstat-detail.sh
Local-Address Foreign-Address State
0.0.0.0:44 0.0.0.0:0 LISTEN
0.0.0.0:23 0.0.0.0:0 LISTEN
0.0.0.0:80 0.0.0.0:0 LISTEN
127.0.0.1:26 0.0.0.0:0 LISTEN
0.0.0.0:6 0.0.0.0:0 LISTEN
127.0.0.1:6 127.0.0.1:6 ESTABLISHED
233.178.12.266:72 222.35.231.188:1257 ESTABLISHED
0.0.0.0:6 0.0.0.0:0 LISTEN
127.0.0.1:6 0.0.0.0:0 LISTEN
0.0.0.0:6 0.0.0.0:0 LISTEN
233.178.12.266:6 0.0.0.0:0 LISTEN
127.0.0.1:6 127.0.0.1:56 ESTABLISHED
[root@localhost tmp]#
[root@localhost tmp]# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:76 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:6 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:6 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:26 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:6 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:6 127.0.0.1:56827 ESTABLISHED
tcp 0 908 233.178.12.266:6 222.35.231.128:1257 ESTABLISHED
tcp 0 0 :::6 :::* LISTEN
tcp 0 0 ::ffff:127.0.0.1:6 :::* LISTEN
tcp 0 0 :::86 :::* LISTEN
tcp 0 0 ::ffff:233.178.12.266:26 :::* LISTEN
tcp 0 0 ::ffff:127.0.0.1:566 ::ffff:127.0.0.1:56822 ESTABLISHED
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 16986424
unix 2 [ ] DGRAM 16986405
unix 2 [ ] DGRAM 4505
unix 2 [ ] DGRAM 4427
还有一个小的细节就是在做if判断是,偶把最频繁出现的放在了第一个比较上,比如tcp的状态比tcp6的多,tcp状态中TIME_WAIT肯定最多(不开启复用的话)所以,都在各自的if中放在了第一个比对条件,据说这样也可以提高效率,没有时间了,要陪老婆去民族园玩啦,呵呵。
阅读(3619) | 评论(0) | 转发(1) |