Win32下的Perl,无用的select,停滞的Tk,结束吧....
摘要: 采编系统客户端网络的改造
关键词:
postgresql
数据库,
vlan
,
acl
,三层交换,路由
...
阅读全文
原先已经有了一台华为s3526e的三层交换机。考虑到兼容的因素,这次还是选用了相同的型号。
令人感到兴奋的是,新的s3526e在功能上有了不小的改进:
首先,增加了dhcp server的功能,就是说可以直接在交换机上做dhcp服务器,以前的及只能做relay,为此还不得不做一台dhcp服务器,麻烦程度可想而知。
第二,增加了am user-bind功能,方便ip地址的绑定。如果用acl的话需要很多步骤,我会在后面讲的。
从aux口接入配置线,另一头接电脑的com口。开启电脑的超级终端,设置为直接连接com口,中断位无,效验位无。这时,交换机可以通电了,超级终端立刻会有反映。
完全开启前可能会需要等待1、2分钟的时间,之后按enter键开始配置过程。
1:
super
sys
super password simple wahaha
sysname gyn_001
user-interf vty 0 4
authentication-mode passw
set authentication passw wahaha
quit
以上步骤为交换机取了个gyn_001的名字,同时设置高级模式3的密码为wahaha,设置telnet接入密码为wahaha
2:
vlan 100
quit
interf v 100
ip addr 192.168.100.65 255.255.255.0
quit
这样就建立了一个三层地址为192.168.100.65的vlan 100
3:
vlan 100
port e0/2
quit
interf e0/2
flow-control
quit
为vlan 77建立access口用来接入电脑,同时启动流控
4:
acl name net-forb-e2 link
rule 0 deny ingress interf e0/2 egress any
quit
packet-filter link-group net-forb-e2
封锁e0/2口,不允许接入电脑
5:
acl name net-mgr-e2 link
rule 0 permit ingress 0011-5b99-ed36 0-0-0 egress any
rule 1 permit ingress 000d-4c4c-5b19 0-0-0 egress any
quit
packet-filter link-group net-mgr-e2
开启mac地址为0011-5b99-ed36和000d-4c4c-5b19的两台电脑上网
以上两个步骤的顺序不可意对调,否则将无法达到控制外来电脑接入的功能。
6:
gvrp
vlan 500
quit
interf v 500
ip addr 192.168.65.21 255.255.255.240
quit
interf e0/1
port link-type trunk
port trunk permit vlan all
gvrp
quit
建立了一个与旧的三层交换机相对应的一个vlan,用来建立路由。这时可以用一根网线将两台交换机连起来了。
7:
ip route-static 192.168.6.0 255.255.255.0 192.168.65.25
ip route-static 192.168.0.200 255.255.255.255 192.168.65.25
建立到旧三层交换机的静态路由。当然最简单的办法就是建立一个缺省路由
ip route-static 0.0.0.0 0.0.0.0 192.168.65.25
只是出于安全的原因,我不希望让该网段的电脑能随便访问别的网段。
8:
在旧交换机上建立到新机器的静态路由
ip route-static 192.168.100.0 255.255.255.0 192.168.65.21
现在大功基本告成了。新交换机所连接的两台电脑只要手动配置ip地址,就可以访问服务器区的电脑并且可以上网了。为了方便其间最好用dhcp来分配并管理ip地址的使用。
9:
dhcp ena
interf v 100
dhcp select interf
这样就在vlan 100中启动了dhcp服务器
为了管理方便,将绑定一些经常搞破坏的电脑的ip地址
10:
acl绑定法:
acl number 1
rule 0 permit source 192.168.100.2 0
quit
acl number 200
rule 0 deny ingress 0011-5b99-ed36 0-0-0 egress any
rule 1 permit ingress 0011-5b99-ed36 0-0-0 egress any
quit
packet-filter link-group 200 rule 0
packet-filter ip-group 0 rule 0 link-group 200 rule 1
am绑定法:
am ena
am user-bind ip_addr 192.168.100.2 mac_addr 0011-5b99-ed36
相比之下am绑定法显然简单不少,但据说很消耗资源,并且可绑定的上限就100多台,所以还是用了acl绑定法。
11:
quit
save
quit
最后,保存并退出。
到这里为止,全部搞定。
之前是装过debian的,很方便的,但对中文的支持不好,所以就放弃了。
RH9已经很老了,所以一直担心对这台相对较新的电脑支持不好,事实上也验证了我的想法。
装完之后,显示器频率只能定在60Hz,相当闪烁。用redhat-config-xfreevid调了一下,不见效果。又在参数文件中锁定85Hz,重启还是没有用。到intel网站下载linux用显卡驱动,是tar.gz包,安装有点麻烦,但还算顺利,重启后依旧没用。最后想起在debian里时,好像是用的vesa驱动,立即试了一下,成功!
声卡无法检测,于是安装alsa万能驱动,重启后发现声卡,但检测发声不正常(放屁状),只有升级内核,这就麻烦了,日后再说。
再root中service postgresql start,解锁postgres用户,重设密码,su进入后,createuser个人用户,允许createdb,不允许createuser,su个人用户。现在可以使用数据库了。
基本上还算可以,就是数据库版本老了点,7.3.4的。在postgresql官方网站上没用找到8.1.3的RH9安装包,连8.0的也没有,估计已经停止支持了。
凑活用吧~~:->
create table net_top(switch_pos int, floor int);
create table net_port(room int primary key, port1 int default 0, port2 int default 0);
create table net_mac_addr(room int references net_port(room),
mac_addr varchar(30) unique check (mac_addr ~ '^(00)[0-9A-F]{10}$'),
ip_addr varchar(30) check (ip_addr ~ '[0-9]{3}(\.[0-9]{1,3}){3}$'),
owner varchar(20)
);
create view net_room_view as
select * from net_top t left outer join net_port p
on (p.room/100=t.floor or p.room=t.floor);
create view net_owner_view as
select n.room, n.mac_addr, n.ip_addr, n.owner, p.port1, p.port2
from net_mac_addr n left outer join net_port p
on (n.room=p.room);
存在'E:\dbase_related\net_mgr.sql'中,然后在命令行中执行
psql -U postgres -f e:\dbase_related\net_mgr.sql
就行了。
局域网电脑管理系统之一
信息数据库的架构
使用
postgresql8.1
建立信息数据库。
建立三张表:
ü
交换机分布表:
net_top
ü
办公室与井道端口对应表:
net_port
ü
mac
地址表:
net_mac_addr
建立的程序如下:
create table net_top(
switch_pos integer,
floor integer
);
create table net_port(
room integer,
port1 integer,
port2 integer
);
create table net_mac_addr(
room integer,
mac_addr varchar(30),
ip_addr varchar(30),
owner varchar(20)
);
建立视图
net_room_port
,将表
1
、
2
连接起来。
create view net_room_view as
select * from net_top t left outer join net_port p
on (p,room/100=t.floor);
给
3
张表添加约束,使之能正确得存储。
在
net_port
中,将
room
设为主键:
alter table net_port add constraint room_prim primary key (room);
在
net_mac_addr
中将
room
设为
net_port.room
的外键:
alter table net_mac_addr
add foreign key (room) references net_port;
在
net_mac_addr
中设置
mac_addr
和
ip_addr
的检查:
alter table net_mac_addr add constraint mac_check check
(mac_addr ~ ‘^(00) [0-9A-Z]{10 }$’);
alter table net_mac_addr add constraint ip_check
check (ip_addr ~ ‘[0-9]{3}(\.[0-9]){1,3}$’);
表建立以后,就要进行数据输入。
1
:输入表
1
的数据:
新建一个文本文键
e:\switch.txt
,在其中输入数据,如下:
2 1
2 2
2 3
6 4
6 5
6 6
8 7
8 8
8 9
8 10
12 11
12 12
12 13
12 14
12 15
保存后,将该文本内容拷贝至表
1
中。
copy net_top from ‘e:/switch.txt’;
2
:输入表
2
的内容
新建一个文本文键
e:\room.txt
,编写一个脚本用来输入房间号:
open(FILE,'>e:/switch.txt');
for($init=1; $init<=15; $init++){
for($temp=1; $temp<=15; $temp++){
$sum=$init*100+$temp;
print FILE "$sum"."\n";
}
}
close FILE;
然后根据大楼井道分布图所示,将端口填入,以下是其中的一部分:
1011040
102 1160
103 1050
104 1030
105 1060
106 0 0
107 1070
108 0 0
109 1080
110 0 0
11 0 0
112 0 0
113 109110
114 0 0
115 0 0
201 2040
202 2010
总共
228
个数据。需要提醒的是,以上所有数据行中的数据都用制表符
分割。保存后,将该文本内容拷贝至表
1
中。
copy net_port from ‘e:/room.txt’;
3
:输入表
3
的内容
这是最艰巨的,需要一台电脑接着一台得打入
ipconfig –all
,然后在逐一输入。为了简化操作,写了一组采集程序。
服务器端的程序如下:
$in_buffer=undef;
$PF_INET=2;
$port=2345;
$local_addr=pack('SnC4x8',$PF_INET,$port,192,168,138,105);
$SOCK_DGRAM=2;
socket(UDP_SERVER,$PF_INET,$SOCK_DGRAM,getprotobyname('udp')) or die("$!");
bind(UDP_SERVER,$local_addr) or die("$!");
listen(UDP_SERVER,100);
print("waiting for connection...\n");
$count=0;
while(1){
print $count." connection..\n";
$count++;
if(recv(UDP_SERVER,$in_buffer,100,0)){
open(FILE,'>>e:/mac_addr.txt');
chomp($in_buffer);
print FILE $in_buffer."\n";
close FILE;
}
else{next;}
}
close(UDP_SERVER);
客户端的程序如下:
@stack=();
$mac_addr=undef;
$ip_addr=undef;
print('
请输入您的办公室门牌号码:
');
$room_num=<STDIN>;
chomp($room_num);
print('
请输入您的姓名拼音:
');
$name=<STDIN>;
chomp($name);
open(FOO,'-|',"ipconfig -all");
while(<FOO>){
chomp();
if($_=~s/(.*)(00(\-[0-9A-Z]{2}){5})(.*)/$2/){
$mac_addr=join('',split(/-/,$_));
}
if($_=~/IP Address/){
$_=~s/(.*)([0-9]{3}(\.[0-9]{1,3}){3})(.*)/$2/;
$ip_addr=$_;
}
}
close FOO;
push(@stack, $room_num);
push(@stack, $mac_addr);
push(@stack, $ip_addr);
push(@stack, $name);
$out_buffer=join("\t",@stack);
print $out_buffer."\n";
$PF_INET=2;
$port=2345;
$remote_addr=pack('SnC4x8',$PF_INET,$port,192,168,138,105);
$SOCK_DGRAM=2;
socket(UDP_CLIENT,$PF_INET,$SOCK_DGRAM,getprotobyname('udp'));
send(UDP_CLIENT,$out_buffer,0,$remote_addr);
close(UDP_CLIENT);
exit;
采集的数据被记录到服务器中:
然后将数据导入数据库:
copy net_mac_addr from ‘e:/mac_addr.txt’;
5月8日的电脑报上有一个小测试:
任何一个整数的立方都可以写成一串连续奇数的和。
想也没想,就写了:
use strict;
my $seed=<STDIN>;
$seed**=3;
my @stack=();
open(NKCS,'>c:/code/nkcs.txt');
for(my $init=1; $init<=$seed; $init+=2){
my $sum=$init;
my $temp=$init;
while($sum<$seed){
push(@stack,$temp);
$temp+=2;
$sum+=$temp;
}
if($sum==$seed){
while(@stack){print NKCS shift(@stack)."\n" ;}
close NKCS;
exit;
}
undef @stack;
}
close NKCS;
die("nkcs failed");
package data_string;
sub new{
shift();
my $val=undef;
my $ref=undef;
($val,$ref)=@_;
my $new_string={
val => $val,
ref => $ref
};
bless $new_string, data_string;
return $new_string;
}
#return a reference to the hash holding two incoming parameters which are value of the node and a #reference to a next node
sub val{
return shift()->{val};
}
#return the value of the node
sub ref{
return shift()->{ref};
}
#return a reference to the following node
sub setval{
my $tnode=shift();
if(@_){
my $tval=shift();
$tnode->{val}=$tval;
return 1;
}
return 0;
}
#set the node with a new value
sub setref{
my $tnode=shift();
my $tref=shift();
if(CORE::ref($tnode)){
$tnode->{ref}=$tref;
return 1;
}
return 0;
}
#set the node with a new reference
sub setnull{
undef %{shift()};
return 1;
}
#undefine the node
sub length{
my $count=0;
my $head=shift();
while( defined($head) ){ $count++; $head=$head->ref;}
return $count;
}
#return the length of a list whose head is the current node
sub insert{
my $head=shift();
my $pos=undef;
my $str=undef;
if(@_==2){
($pos, $str)=@_;
if($pos>=$head->length && $pos<0)
{return 0;}
else
{
my $pre=$head;
my $cur=$pre->ref;
my $index=0;
while($index!=$pos && defined($cur)){
$pre=$cur;
$cur=$cur->ref;
$index++;
}#while
$pre->setref(data_string->new($str, $cur));
return 1;
}#else
}
return 0;
}
#insert the current node leading list a new node taking the incoming parameter as the value
sub delete{
if($_[1]==0){
if($_[0]->length==1){shift()->setnull; return 1;}
else{
my $fir=shift;
my $sec=$fir->ref;
$fir->setval($sec->val);
$fir->setref($sec->ref);
$sec->setnull;
return 1;
}
}
my $head=shift();
if(@_){
my $pos=shift();
if($pos>=$head->length && $pos<=0)
{return 0;}#if
else{
my $pre=$head;
my $cur=$pre->ref;
my $index=1;
while($index<$pos){
$pre=$cur;
if(defined($cur)){$cur=$cur->ref;}
$index++;
}#while
$pre->setref($cur->ref);
$cur->setnull;
return 1;
}#else
}#if
return 0;
}
#delete the current node leading list a node whose position is related to the incoming parameter
1;
#写了个测试程序:
use data_string;
$temp=undef;
$node=data_string->new(1, $temp);
foreach (qw(2 3 4 5)){
$ind=$_-2;
if($ind<$node->length){
$node->insert($ind, $_);
}
}
$node->delete(3);
$temp=$node;
while(defined($temp)){
print($temp->val."\n");
$temp=$temp->ref;
}
sub sinode{
my %node=(val=>undef, ref=>undef);
if(@_>1){
$node{val}=shift();
$node{ref}=shift();
}
else{
if(@_=1){
$node{val}=shift();
}
}
return \%node;
}
$temp=undef;
foreach (qw(5 4 3 2 1)){
$node=sinode($_, $temp);
$temp=$node;
}
do{
printf("$node->{val}\n");
$node=$node->{ref};
}until($node eq undef)
oracle-plsql
异常处理(
4
)
异常处理
当异常生成之后,程序被中止,控制权交给异常处理模块,异常处理模块捕获当前异常句柄,并交由相应的程序处理;如果,异常促里模块没有捕捉到异常句柄,那么它将被传输到当前程序的外围。
除非由一些特殊的要求,一般情况下异常将再当前程序的异常处理模块中被处理。异常处理模块以
EXCEPTION
开始
END;
结尾。
Declare
/*…………*/
begin
/*…………*/
exception
when /*
异常名称
*/
then /*
异常处理
*/
when other
then /*
异常处理
*/
end;
异常处理模块的语法基本上以
CASE
一致,凡是在
when
中有定义的异常都将被处理,而没有的则被传输。一个特殊的异常处理语句是
WHEN OTHERS
。就想在(
3
)中所说的,它会处理所有为被处理的异常,因此必须小心使用它,最好是在最外层的程序中。当然如果喜欢偷懒的,大可以在异常处理模块中只放一个
OTHERS
。注意,无论哪种情况,
OTHERS
只能这只在异常处理的最后一位。
有趣的是,可以在一个
when
中处理多个异常句柄。
Exception
When no_data_found or invalid_employee_id or dbms_ldap.invalid session
Then /*………..*/
End;
/
在这个例子里,有标准包的异常、自定义异常和非标准包中的异常。这些异常只能用
or
连接,不可以用
and
,因为只有一个异常能够生成。
非
raise_application_error
生成的异常,如果没有被处理而一直传递到系统环境中,那么环境将视情况作出相应的反映。在
sqlplus
中,
oracle
将回滚所有
DML
对数据所做的修改。在
sqlplus
环境中,因为有自动回滚的存在,我们可以保留出现未被处理的异常的可能性;而在另外的一些环境中,则需要仔细设计最外层程序。
ü
捕捉任何有可能传出的异常。
ü
记录错误以便于分析。
ü
给外部环境一个信息,以便于其作出相应的处理。
对于自定义异常,因为
sqlcode
值永远是
1
,所以当它被传出时,如果外围程序中没有定义相同名称的异常,我们将不知道是什么异常产生了。因此,不要将自定义异常传递出去。
在程序中处理几个互相独立的操作时,为了避免出现因为一个操作产生异常而使整个程序被中断的情况,有必要将这些独立的操作放在各自的虚拟块中。
Procedure change_data is
Begin
Begin
Delete from employee where …..
Exception
When others then null;
End;
Begin
Update company set …….
Exception
When others then null;
End;
Begin
Insert into company_history select * from company where ….
Exception
When others then null;
End;
End;
/
Pl/sql
提供了一些内建的函数来帮助我们确定、分析异常。
SQLCODE
这个函数在前面有提到过,它是一个用于返回当前模块中最近一次异常值的函数,或者说是非入栈程序的异常值。打个比方:如果在当前程序的异常模块中调用了另一个程序,
oracle
将当前程序及相应的环境变量(包括异常值)压入系统栈;在被调用程序中生成了一个值为
1
的异常,那么
sqlcode
将返回
1
;之后刚才的程序出栈,
sqlcode
返回当前异常值。需要注意的是,不要在异常模块之外使用它,这样不会有任何意义。当没有异常或在异常模块之外使用时,
SQLCODE
返回
0
;返回值
1
是指自定义异常。
SQLERRM
接收异常值,返回相应的长度不超过
512
字节的描述语。如果没有传入异常值,则返回当前异常描述。
Begin
Dbms_output.put_line( sqlerrm(-1403);
End;
Sql>/
Ora-1403: no data found
在需要体构长度超过
512
字节的描述时,
oracle
建议使用
dbms_utility.format_error_stack
。显然,用这个函数来判断一个异常是否为系统异常是很有用的,如果不是的话,将返回以下两种情况的一种。
如果是一个负数:
ora-nnnnn: message not found,; product=rdbms; facility=ora
如果是一个正数:
-nnnnn: non-oracle exception
DBMS_UTILITY.FORMAT_ERROR_STACK
返回当前异常相应的描述,没有字符长度限制。与
SQLCODE
相同的是,必须在异常处理模块中使用。虽然名称中有一个
stack
在,但通过它并不能知道异常的最初生成处,需要的话就必须使用
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
。
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
系统为最近一次生成的异常设置了一个栈,并跟踪它的传递过程,而这个函数使用这个栈,然后返回该异常的整个传递过程。这个函数对错误的定位和实施下一步处理起着至关重要的作用。
Create or replace procedure procl is
Begin
Dbms_output.put_line(‘running proc1’);
Raise no_data_found;
End;
/
create or replace procedure proc2 is
begin
dbms_output.put_line(‘calling proc1’);
proc1;
end;
/
create or replace procedure proc3 is
begin
dbms_output.put_line(‘calling proc2’);
proc2;
exception
when no_data_found
then
dbms_output.put_line(‘error stack at top level’);
dbms_output.put_line(dbms_utility.format_error_backtrace);
end;
/
现在可以运行
proc3
来看看结果。
Sql>set serveroutput on;
Sql>begin
2
dbms_output.put_line(‘proc3->proc2->proc1 backtrace’);
3
proc3;
4 end;
5 /
Proc3 -> Proc2 -> Proc1 backtrace
calling proc2
calling proc1
running proc1
Error stack at top level:
ORA-06512: at "SCOTT.PROC1", line 4
ORA-06512: at "SCOTT.PROC2", line 5
ORA-06512: at "SCOTT.PROC3", line 4
事实上,每次异常的产生都将重置这个异常栈,只是最后一次从系统栈出栈的是最外层的程序块,所以可以清楚地看到异常生成的整个过程。上面这个程序的执行过程是这样的:首先用
put_line
打印
Proc3 -> Proc2 -> Proc1 backtrace
,
调用
proc3
,当前程序入栈
=>
打印
calling proc2
,调用
proc2
,
proc3
入栈
=>
打印
calling proc1
,调用
proc1
,
proc2
入栈
=>
打印
running proc1
,生成
no_data_found
异常,该异常被压入异常栈中
=> proc2
出栈,并检测到来自第
5
行调用传递过来的异常,将它在此压入异常栈
=> proc3
出栈,并检测到来自第
4
行调用传递过来的异常,将它在此压入异常栈,
dbms_utility.format_error_backtrace
将异常栈中信息反相打印出来
=>
最外层程序出栈,
end
。
以下是正确使用这个函数的一些注意事项:
ü
在当前程序的异常处理模块中调用这个函数。
ü
避免在中间程序中使用异常处理模块。
这样异常就能被正确地传输到最外层程序中,并打印出这个过程了。
Oracle:pl/sql
异常处理 (3)
生成错误
处理
oracle
系统自动生成系统异常外,可以使用
raise
来手动生成错误。
l
Raise exception;
l
Raise package.exception;
l
Raise;
以上是
raise
的三种使用方法。第一种用于生成当前程序中定义的异常或在
standard
中的系统异常。
Declare
Invalid_id exception;
Id_values varchar(2);
Begin
Id_value:=id_for(‘smith’);
If substr(id_value,1,1)!=’x’
Then
Raise invalid_id;
End if;
Exception
When invalid_id
Then
Dbms_output.put_line(‘this is an invalid id!’);
End;
这是一个生成自定义异常的例子,当然也可以生成系统异常:
declare
employee_id_in number;
Begin
Select employee_id into employee_id_in from employ_list where employee_name=&n;
If employee_id_in=0
Then
Raise zero_devided;
End if;
Exception
When zero_devided
Then
Dbms_output.put_line(‘wrong!’);
End;
有一些异常是定义在非标准包中的,如
UTL_FILE
,
DBMS_SQL
以及程序员创建的包中异常。可以使用
raise
的第二种用法来生成异常。
If day_overdue(isbn_in, browser_in) > 365
Then
Raise overdue_pkg.book_is_lost
End if;
在最后一种
raise
的形式中,不带任何参数。这种情况只出现在希望将当前的异常传到外部程序时。
Exception
When no_data_found
Then
Raise;
End;
Pl.sql
使用
raise_application_error
过程来生成一个有具体描述的异常。当使用这个过程时,当前程序被中止,输入输出参数被置为原先的值,但任何
DML
对数据库所做的改动将被保留,可以在之后用
rollback
命令回滚。下面是该过程的原型:
Procedure raise_application_error(
Num binary_integer;
Msg varchar2;
Keeperrorstack Boolean default false
)
其中
num
是在
-20999
到
-20000
之间的任何数字(但事实上,
DBMS_OUPUT
和
DBMS_DESCRIBLE
包使用了
-20005
到
-20000
的数字);
msg
是小于
2K
个字符的描述语,任何大于
2K
的字符都将被自动丢弃;
keeperrorstack
默认为
false
,是指清空异常栈,再将当前异常入栈,如果指定
true
的话就直接将当前异常压入栈中。
CREATE OR REPLACE PROCEDURE raise_by_language (code_in IN PLS_INTEGER)
IS
l_message error_table.error_string%TYPE;
BEGIN
SELECT error_string
INTO l_message
FROM error_table, v$nls_parameters v
WHERE error_number = code_in
AND string_language = v.VALUE
AND v.parameter = 'NLS_LANGUAGE';
RAISE_APPLICATION_ERROR (code_in, l_message);
END;