2005年9月24日
#
正则表达式是一种模式匹配形式,它通常用在处理的文本程序中。比如我们经常使用的grep工具,还是perl语言都使用了正则表达式。传统的C++处理正则表达式是非常麻烦的,这也成为很多其他语言爱好者的笑柄,现在情况不一样了,因为有了boost。
Boost是一个基于Template的开发源代码库,在这个库中有很多子库用来高效处理各方面的问题,比如字符串拆分,格式化,线程等等,Boost对于每一个C++爱好者都是应该了解的,对于C++ Builder用户如果能在熟练使用VCL的情况下再熟练使用Boost,我想一定如虎添翼。
一般来说,使用Boost是非常简单,和使用其他STL库没有太大区别,但使用Boost的正则表达式库则不那么容易,因为这个库还需要我们单独编译,下面我将详细介绍如何使用。
如果你还不知道或者还没有Boost的话,你可以去www.boost.org下载最新版本,作者使用的是1.30版本。将下载下来的zip包[1]解压到任何你喜欢的目录,比如D:\boost。
编译正则表达式库
前面已经提到,这个库需要我们单独编译才能使用,为什么不编译好一起发布呢?主要是考虑到不同的编译器需要不同的链接库文件和链接库太大了。在命令行下,进入[%Boost]\Libs\RegEx\Build目录,直接敲入make –fbcb6.mak命令开始编译,这里请大家注意了,如果你的计算机上同时安装了BCB5,请一定要把path设置成为BCB6的bcc32.exe程序所在的目录,否则可能使用BCB5的make程序,这样虽然能编译但最后不能使用。
编译过程相当耗时,你需要耐心等待,最终编译完成,会在[%Boost]\Libs\RegEx\Build目录生成一个BCB6目录,在这个目录生成了很多lib文件和dll文件,把所有dll文件复制到windows系统目录,所以lib文件复制到bcb6\lib目录。如果你不想这么麻烦的复制文件,可以在编译时加入install参数,就像这样make –fBcb6.mak install,不过作者还是比较喜欢前一种方式,这样我可以知道到底生成了什么文件。现在编译已经完成了,你可以体现boost的神奇魅力了。
#include<deque>
#include<iostream>
#include<algorithm>
#include<boost/regex.hpp>
int main()
{
using namespace boost;
using namespace std;
regex expression("\\s+href\\s*=\\s*\"([^\"]*)\"",regbase::normal|regbase::icase);
string s="<a href=\"index.html\"><img src=\"logo.gif\"></a>";
deque<string> result;
regex_split(std::back_inserter(result),s,expression);
copy(result.begin(),result.end(),ostream_iterator<string>(cout,"\n"));
int c;
cin>>c;
return 0;
}
设置BCB6 Project属性的Lib Path和Include Path为你安装boost的目录,运行你会看到结果:
index.html
可以看到index.html已经从字符串中提出出来了,那么为什么会是这样呢?
代码的核心部分是:
regex expression("\\s+href\\s*=\\s*\"([^\"]*)\"",regbase::normal|regbase::icase);
它用来设置如何匹配字符串,上面乱七八糟的字符串很难看懂,如果不了解正则表达式的书写规则,上
面代码可以和天书媲美。
regbase::normal|regbase::icase 是解析参数设置,具体可以参考boost帮助文档。
正则表达式的书写规则
具体的书写规则,大家可以参看boost的文档,我这里做一下简要说明:
. (dot)
用来匹配任何一个字符,但不包括新行上的字符
*
闭包,任意有限次的自重复连接
+
有限次自重复连接,但至少出现一次
{}
指定可能的重复次数
例如:
ba* 匹配 b ba baa baaa等
ba+ 匹配 ba baa baaaaaaaaa等
ba{1,5} 匹配 ba baa baaa baaaa baaaaa
\
转义字符,有很多用途,根据参数设置而变化,最常见的就是类似于c语言\的用法
\s
匹配空格
\w
匹配一个单词
\d
匹配数字
()
有两种用法:
1是合并的作用,例如(ab)*匹配ab abab ababab等
2是确定匹配,也就是说在()中的字符将被最终拆解出来
根据上面这张表,我们可以很容易知道前面的那段天书如何解释。
一个实际的例子
前一段时间在CSDN上有一篇帖子,问题是有一种文件结构如(类似):
@People{
Age=19
Speek=”Hay,{name},how are you”
}
问如何拆分字符串得到@后面的名字,=两边的属性名和属性值,引号里{}种的名字。
解决这个问题用正则表达式再合适不过了。
根据分析,我们可以这样构造匹配规则:
"@(.*?)\s*\\{" 匹配@开始的字符创,后面两种类型如何构造匹配规则留给大家思考吧。
这样我们可以轻易拆解这个例子。
性能分析
通过上面的讨论,大家已经了解到boost的强大威力,那个性能又如何呢?为此我们再实际来拆分一个
复杂的html代码,看看到底需要花费多少时间。
为了节省篇幅,这里就不列出html代码了,不过可以告诉大家,这是一个又Word生成的大小为186K
的html文件,这个文件中用到了很多<table>标签,所以我这里测试就来拆分所有<table>标签的
width属性。测试代码如下:
#include<deque>
#include<iostream>
#include<algorithm>
#include<boost/regex.hpp>
#include<vcl.h>
int main()
{
using namespace boost;
using namespace std;
TStringList* html=new TStringList();
html->LoadFromFile("D:\\1.htm");
regex expression("\\s+width=([^\"]*)\s+",regbase::normal|regbase::icase);
DWORD start=GetTickCount();
for(int n=0;n<html->Count;n++)
{
string s=html->Strings[n].c_str();
deque<string> result;
regex_split(std::back_inserter(result),s,expression);
copy(result.begin(),result.end(),ostream_iterator<string>(cout,"\n"));
result.clear();
}
start=GetTickCount()-start;
delete html;
cout<<start;
int c;
cin>>c;
return 0;
}
输出结果为671毫秒,拆分得到1072个width属性值,我们可以看到boost的效率是非常高的,虽然与一些角本语言比起来解析速度还是慢,但已经可以满足大多数编程要求了。另外作者的计算机配置并不是非常高,相信拿到现在任何一台主流配置的计算机上都会优于作者的结果。
结束语
其实上面的强大威力只是boost的冰山一角,如果你不自己去体会,你很难想象到boost的强大威力。在boost里还有很多使用的库,比如格式化输出,字符串拆解,类型转换等,这些库使用起来也比较方便,大家可以自行参考boost文档。在这些库中还有两个库需要自行编译,他们是Python和thread库,而且这些库的编译需要专门的工具Jam,所以我们在编译这些库的时候还要编译jam工具,而编译jam工具也不是一件快乐的事情,麻烦同样出现在如果你安装了多个编译器,如果读者有兴趣可以自己试一下。
不过BCB6并不支持全部boost库,从boost提供的编译器支持表可以看到[2],BCB6还是有相当多的库不支持的,支持最好的是gcc/g++的编译器,但也不是全部支持。希望borland下一个将要发布的C++编译器可以支持更多C++标准。
[1] 其实还有其他类型的包,但在windows系统下,你最好下载zip包
[2] Boost提供的编译器支持表是针对BCB5的,对于BCB6的支持作者并没有详细测试,如果读者有兴趣可以自己测试boost附带的测试代码。
根据网上各种文档整理而成.=号两边要空格的问题折磨了我好久.
1:安装
先检查是否安装CVS包
#>rpm -qa|grep cvs
没有安装的话,用下面2种方法安装
(1):在安装linux的时候可以选择安装CVS包
(2):另外下载CVS RPM包 自行安装
2:建立cvs用户和组
#> groupadd cvs
#> useradd -g cvs -G cvs –d /cvsroot cvsroot
#> passwd cvsroot
更改目录属性
chmod –R 770 /cvsroot
3:建立CVS服务
#more /etc/services | grep cvspserver
看看是否有
cvspserver 2401/tcp #CVS client/server operations
cvspserver 2401/udp #CVS client/server operations
如果没有需要到/etc/service文件中增加
建立#vi /etc/xinet.d/cvspserver 文件内容如下
service cvspserver
{
disable = no
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/bin/cvs
server_args = -f --allow-root=/cvsroot pserver
}
该文件有特别要注意的地方,所有=号两边都需要空一个空格,除了"root=/cvsroot" 所有要空格的地方,不要多加空格.否则会有CVS服务不能启动的问题
切换到cvsroot用户
#cvs -d /cvsroot init
然后重新启动xinetd服务或者重启动机器
#service xinetd restart
然后用
#netstat -l | grep cvspserver
or
#netstat -l | grep 2401
看是否有下面tcp 0 0 *:cvspserver *:* LISTEN
说明已经正常启动,没有的话请重新检查配置过程是否有错误或者遗漏。最后还必须检查防火墙的设置,把2401端口打开。
4:用户管理
CVS默认使用系统用户登录,所有系统用户都可以登陆,但是这样对系统不安全,我们需要独立的用户管理.CVS用户名和密码保存在CVSROOT目录下的passwd文件中.格式
用户名:密码:系统用户
#htpasswd passwd username
用来设置用户密码并保存到passwd文件中.
然后需要关闭系统用户登陆使用cvs的权限,CVSROOT目录下的config文件,把#SystemAuth=no的#去掉就可以了.
测试登陆
#cvs -d “:pserver:username@127.0.0.1:/cvsroot” login
ok
5 :源代码仓库的备份和移动
基本上,CVS的源代码仓库没有什么特别之处,完全可以用文件备份的方式进行备份。需要注意的只是,应该确认备份的过程中没有用户提交修改,具体的做法可以是停止CVS服务器或者使用锁等等。恢复时只需要把这些文件按原来的目录结构存放好,因为CVS的每一个模块都是单独的一个目录,与其他模块和目录没有任何瓜葛,相当方便。甚至只需要在仓库中删除一个目录或者文件,便可以删除该模块的一些内容,不过并不建议这么做,使用CVS的删除功能将会有一个历史记录,而对仓库的直接删除不留任何痕迹,这对项目管理是不利的。移动仓库与备份相似,只需要把该模块的目录移动到新的路径,便可以使用了。
如果不幸在备份之后有过一些修改并且执行了提交,当服务器出现问题需要恢复源代码仓库时,开发者提交新的修改就会出现版本不一致的错误。此时只需要把CVS相关的目录和文件删除,即可把新的修改提交。
6.更进一步的管理
CVSROOT目录下还有很多其他功能,其中最重要的就是modules文件。这个文件定义了源代码库的模块,下面是一个例子:
代码: |
Linux Linux
Kernel Linux/kernel |
这个文件的内容按行排列,每一行定义一个模块,首先是模块名,然后是模块路径,这是相对于CVS根目录的路径。它定义了两个模块,第一个是Linux模块,它位于Linux目录中,第二个是Kernel模块,这是Linux模块的子模块。
modules文件并非必须的,它的作用相当于一个索引,部分CVS客户端软件通过它可以快速找到相应的模块,比如WinCVS。
7.协同开发的问题
默认方式下,CVS允许多个用户编辑同一个文件,这对一个协作良好的团队来说不会有什么问题,因为多个开发者同时修改同一个文件的同一部分是不正常的,这在项目管理中就应该避免,出现这种情况说明项目组内部没有统一意见。而多个开发者修改文件的不同部分,CVS可以很好的管理。
如果觉得这种方式难以控制,CVS也提供了解决办法,可以使用cvs admin -l进行锁定,这样一个开发者正在做修改时CVS就不会允许其他用户checkout。这里顺便说明一下文件格式的问题,对于文本格式,CVS可以进行历史记录比较、版本合并等工作,而二进制文件不支持这个操作,比如word文档、图片等就应该以二进制方式提交。对于二进制方式,由于无法进行合并,在无法保证只有一个用户修改文件的情况下,建议使用加锁方式进行修改。必须注意的是,修改完毕记得解锁。
从1.6版本开始,CVS引入了监视的概念,这个功能可以让用户随时了解当前谁在修改文件,并且CVS可以自动发送邮件给每一个监视的用户告知最新的更新。
8.建立多个源代码仓库
如果需要管理多个开发组,而这些开发组之间不能互相访问,可以有2个办法:
a.共用一个端口,需要修改cvspserver文件,给server_args指定多个源代码路径,即多个—allow-root参数。由于xinetd的server_args长度有限制,可以在cvspserver文件中把服务器的设置重定向到另外一个文件,如:
代码: |
server = /home/cvsroot/cvs.run |
然后创建/home/cvsroot/cvs.run文件,该文件必须可执行,内容格式为:
代码: |
#!/bin/bash
/usr/bin/cvs -f \
--allow-root=/home/cvsroot/src1 \
--allow-root=/home/cvsroot/src2 \
pserver |
注意此时源代码仓库不再是/home/cvsroot,进行初始化的时候要分别对这两个仓库路径进行初始化,而不再对/home/cvsroot路径进行初始化。
b.采用不同的端口提供服务
重复第2步和第3步,为不同的源代码仓库创建不同服务名的启动脚本,并为这些服务名指定不同的端口,初始化时也必须分别进行初始化。
|
|
|
Oracle 内存分配建议
关于SGA设置的一点总结
本总结不针对特例,仅对服务器只存在OS + ORACLE 为例,如果存在其他应用请酌情考虑
写这个也是因为近来这种重复性的问题发生的太多所导致的
首先不要迷信STS,SG,OCP,EXPERT 等给出的任何建议、内存百分比的说法
基本掌握的原则是, data buffer 通常可以尽可能的大,shared_pool_size 要适度,log_buffer 通常大到几百K到1M就差不多了
设置之前,首先要明确2个问题
1: 除去OS和一些其他开销,能给ORACLE使用的内存有多大
2:oracle是64bit or 32 bit,32bit 通常 SGA有 1.7G 的限制(某些OS的处理或者WINDOWS上有特定设定可以支持到2G以上甚至达到3.7G,本人无这方面经验)
下面是我的windows2000下的oracle :
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE 8.1.7.0.0 Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production
SQL>
windows上存在32bit的限制,如AIX、HP UNIX 等有明确的64BIT OS and ORACLE的版本,32bit oracle可以装在64bit os 上,64 bit oracle不能装在32 bit OS上
不管oracle是32 bit ORACLE还是 64 bit 的,假定应用存在没有很好的使用bind var 的情况,也不能设置 shared_pool_size 过大,通常应该控制在200M--300M,如果是 ORACLE ERP 一类的使用了很多存储过程函数、包 ,或者很大的系统,可以考虑增大shared_pool_size ,但是如果超过500M可能是危险的,达到1G可能会造成CPU的严重负担,系统甚至瘫痪。所以shared_pool_size 如果超过300M还命中率不高,那么应该从应用上找原因而不是一味的增加内存,shared_pool_size 过大主要增加了管理负担和latch 的开销。
log_buffer : 128K ---- 1M 之间通常问题不大,不应该太大
large_pool_size :如果不设置MTS,通常在 RMAN 、OPQ 会使用到,但是在10M --- 50M 应该差不多了。假如设置 MTS,则由于 UGA 放到large_pool_size 的缘故,这个时候依据 session最大数量和 sort_ares_size 等参数设置,必须增大large_pool_size 的设置,可以考虑为 session * (sort_area_size + 2M)。这里要提醒一点,不是必须使用MTS,我们都不主张使用MTS,尤其同时在线用户数小于500的情况下。
java_pool_size : 若不使用java,给30M通常就够了
data buffer ,在做了前面的设置后,凡可以提供给oracle的内存,都应该给data buffer = (db_block_size * db_block_buffers)
在9i 中可以是 db_cache_size
还有2个重要参数我们需要注意
sort_area_size and hash_area_size
这两个参数在非MTS下都是属于PGA ,不属于SGA,是为每个session单独分配的,在我们的服务器上除了OS + SGA,一定要考虑这两部分
(****) : OS 使用内存+ SGA + session*(sort_area_size + hash_area_size + 2M) < 总物理RAM 为好
这样归结过来,假定oracle是 32 bit ,服务器RAM大于2G ,注意你的PGA的情况,,则建议
shared_pool_size + data buffer +large_pool_size + java_pool_size < 1.6G
再具体化,注意满足上面(****) 的原则的基础上可以参考如下设置
如果512M RAM
建议 shared_pool_size = 50M, data buffer = 200M
如果1G RAM
shared_pool_size = 100M , data buffer = 500M
如果2G
shared_pool_size = 150M ,data buffer = 1.2G
物理内存再大已经跟参数没有关系了
假定64 bit ORACLE
内存4G
shared_pool_size = 200M , data buffer = 2.5G
内存8G
shared_pool_size = 300M , data buffer = 5G
内存 12G
shared_pool_size = 300M-----800M , data buffer = 8G
以上仅为参考值,不同系统可能差异比较大,需要根据具体情况调整。建议在设置参数的同时,init中使用 lock_sga ,在不同的平台上可能有不同的方式,使得SGA锁定在物理内存中而不被放入 SWAP 中,这样对效率有好处
关于内存的设置,要再进行细致的调整,起的作用不大,但可根据statspack信息和v$system_event,v$sysstat,v$sesstat,v$latch 等view信息来考虑微调
|
|
摘要:
是谁"偷偷的"用了那么多空间呢(本来有几十个G的Free磁盘空间的)?
检查数据库表空间占用空间情况:
SQL> select tablespace_name,sum(bytes)/1024/1024/1024 GB
2 from dba...
阅读全文
最近在配置MQ,记下了一些常用的MQ命令,如下:
创建队列管理器
crtmqm –q QMgrName
-q是指创建缺省的队列管理器
删除队列管理器
dltmqm QmgrName
启动队列管理器
strmqm QmgrName
如果是启动默认的队列管理器,可以不带其名字
停止队列管理器
endmqm QmgrName 受控停止
endmqm –i QmgrName 立即停止
endmqm –p QmgrName 强制停止
显示队列管理器
dspmq –m QmgrName
运行MQ命令
runmqsc QmgrName
如果是默认队列管理器,可以不带其名字
往队列中放消息
amqsput QName QmgrName
如果队列是默认队列管理器中的队列,可以不带其队列管理器的名字
从队列中取出消息
amqsget QName QmgrName
如果队列是默认队列管理器中的队列,可以不带其队列管理器的名字
启动通道
runmqchl –c ChlName –m QmgrName
启动侦听
runmqlsr –t TYPE –p PORT –m QMgrName
停止侦听
endmqlsr -m QmgrName
下面是在MQ环境中可以执行的MQ命令(即在runmqsc环境下可以敲的命令)
定义持久信队列
DEFINE QLOCAL(QNAME) DEFPSIST(YES) REPLACE
设定队列管理器的持久信队列
ALTER QMGR DEADQ(QNAME)
定义本地队列
DEFINE QL(QNAME) REPLACE
定义别名队列
DEFINE QALIAS(QALIASNAME) TARGQ(QNAME)
远程队列定义
DEFINE QREMOTE(QRNAME) +
RNAME(AAA) RQMNAME(QMGRNAME) +
XMITQ(QTNAME)
定义模型队列
DEFINE QMODEL(QNAME) DEFTYPE(TEMPDYN)
定义本地传输队列
DEFINE QLOCAL(QTNAME) USAGE(XMITQ) DEFPSIST(YES) +
INITQ(SYSTEM.CHANNEL.INITQ)+
PROCESS(PROCESSNAME) REPLACE
创建进程定义
DEFINE PROCESS(PRONAME) +
DESCR(‘STRING’)+
APPLTYPE(WINDOWSNT)+
APPLICID(’ runmqchl -c SDR_TEST -m QM_ TEST’)
其中APPLTYPE的值可以是:CICS、UNIX、WINDOWS、WINDOWSNT等
创建发送方通道
DEFINE CHANNEL(SDRNAME) CHLTYPE(SDR)+
CONNAME(‘100.100.100.215(1418)’) XMITQ(QTNAME) REPLACE
其中CHLTYPE可以是:SDR、SVR、RCVR、RQSTR、CLNTCONN、SVRCONN、CLUSSDR和CLUSRCVR。
创建接收方通道
DEFINE CHANNEL(SDR_ TEST) CHLTYPE(RCVR) REPLACE
创建服务器连接通道
DEFINE CHANNEL(SVRCONNNAME) CHLTYPE(SVRCONN) REPLACE
显示队列的所有属性
DISPLAY QUEUE(QNAME) [ALL]
显示队列的所选属性
DISPLAY QUEUE(QNAME) DESCR GET PUT
DISPLAY QUEUE(QNAME)MAXDEPTH CURDEPTH
显示队列管理器的所有属性
DISPLAY QMGR [ALL]
显示进程定义
DISPLAY PROCESS(PRONAME)
更改属性
ALTER QMGR DESCR(‘NEW DESCRIPTION’)
ALTER QLOCAL(QNAME) PUT(DISABLED)
ALTER QALIAS(QNAME) TARGQ(TARGQNAME)
删除队列
DELETE QLOCAL(QNAME)
DELETE QREMOTE(QRNAME)
清除队列中的所有消息
CLEAR QLOCAL(QNAME)
以下是一些高级配置的命令:
amqmcert 配置SSL证书
amqmdain 配置windows上的MQ服务
crtmqcvx 转换数据
dmpmqaut 转储对象权限管理
dmpmqlog 转储日志管理
dspmq 显示队列管理器
dspmqaut 显示打开对象的权限
dmpmqcap 显示处理程序容量和处理程序数
dspmqcsv 显示命令服务器状态
dspmqfls 显示文件名
dspmqtrc 跟踪MQ输出(HP-UNIX LINUX Solaris)
dspmqrtn 显示事务的详细信息
endmqcsv 停止队列管理器上的命令服务器
strmqcsv 启动队列管理器上的命令服务器
endmqtrc 停止跟踪
rcdmqimg 向日志写对象的映像
rcmqobj 根据日志中的映像重新创建一个对象
rsvmqtrn 提交或逆序恢复事务
如果是字串转换.BCB有多个方法(如TStringConverter,或API的 WideCharToMultiByte/MultiByteToWideChar,VCL的WideCharToString/StringToWideChar等)
当然,BCB本身就支持三种字串,前两种可自动转,后一种有函数:
String x; //GBK
WideString y; //unicode
UTF8String z; //utf8
x=y; //自动
y=x; //自动
z=AnsiToUtf8(x);
x=Utf8ToAnsi(z);
摘要: 在安装过程中出现的一些问题的解决办法。值得收藏与学习。比如在安装的时候如果有中文的路径则会出现类似这样的提示:加载数据库时出错:areasQueries
Oracle的系统要求
企业版:CPU 最低PENTIUM 200M 推荐 PENTIUMIII 1G以上
内存 最低128M 推荐 512M
硬盘空间 系统盘140M 安装盘4.5G(FAT32)或2.75G(...
阅读全文
在经历了一段时间的起起落落以后,开始静下心来回顾近4年的往事,也想到了把东京爱情故事下载了下来看。用了1天时间把这部电视看完了。莉香的笑容确实让人感到阳光灿烂;我觉得她最大的错误,是喜欢了本来就不属于她的“丸子”。从故事一开始,就可以知道,完治的心留在了同学“里美”那里了,对于完治来说,莉香的出现,只是生命中的一个小小插曲。
比尔~盖茨最聪明的地方不是他做了什么,而是他没做什么。他可以做许许多多的事情,却只专注在自己的操作系统,软件研发二不被市场中别的诱惑吸引。
做人要谦卑,做事要学会不断找问题--比尔·盖茨
在真理面前的谦卑,是比尔·盖茨一种内心态度,远比外面的风光无限、备受世人崇敬更重要。
有了在真理面前的谦卑,就可以在这个浮躁的世界中保持一颗安静的心灵,有更大的创造力和影响力。
Linux系统环境下的Socket编程详细解析
什么是Socket
Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。
Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
Socket建立
为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:
int socket(int domain, int type, int protocol);
domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值"0"。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。
Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。
两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。
Socket配置
通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。面向连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。
Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。Bind函数原型为:
int bind(int sockfd,struct sockaddr *my_addr, int addrlen); Sockfd是调用socket函数返回的socket描述符,
my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;
addrlen常被设置为sizeof(struct sockaddr)。 struct sockaddr结构类型是用来保存socket信息的: struct sockaddr { unsigned short sa_family; /* 地址族, AF_xxx */ char sa_data[14]; /* 14 字节的协议地址 */ }; sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data 则包含该socket的IP地址和端口号。 另外还有一种结构类型: struct sockaddr_in { short int sin_family; /* 地址族 */ unsigned short int sin_port; /* 端口号 */ struct in_addr sin_addr; /* IP地址 */ unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ };
|
这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
使用bind函数时,可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:
my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则不需要转换。
计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。
下面是几个字节顺序转换函数:
·htonl():把32位值从主机字节序转换成网络字节序 ·htons():把16位值从主机字节序转换成网络字节序 ·ntohl():把32位值从网络字节序转换成主机字节序 ·ntohs():把16位值从网络字节序转换成主机字节序
|
Bind()函数在成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,你可以选择大于1024中的任何一个没有被占用的端口号。
连接建立
面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接,其函数原型为:
int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
Sockfd是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。Connect函数在出现错误时返回-1,并且设置errno为相应的错误码。进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到打断口。
Connect函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。无连接协议从不建立直接连接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客户的请求。
Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。
int listen(int sockfd, int backlog);
Sockfd是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。Backlog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。如果一个服务请求到来时,输入队列已满,该socket将拒绝连接请求,客户将收到一个出错信息。
当出现错误时listen函数返回-1,并置相应的errno错误码。
accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。
int accept(int sockfd, void *addr, int *addrlen);
sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。
首先,当accept函数监视的socket收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的初始socket仍可以继续在以前的 socket上监听,同时可以在新的socket描述符上进行数据传输操作。
数据传输
Send()和recv()这两个函数用于面向连接的socket上进行数据传输。
Send()函数原型为:
int send(int sockfd, const void *msg, int len, int flags);
Sockfd是你想用来传输数据的socket描述符;msg是一个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0(关于该参数的用法可参照man手册)。
Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send()返回值与len不匹配时,应该对这种情况进行处理。
char *msg = "Hello!";
int len, bytes_sent;
……
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
……
recv()函数原型为:
int recv(int sockfd,void *buf,int len,unsigned int flags);
Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址。
Sendto()函数原型为:
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
Recvfrom()函数原型为:
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回
1,并置相应的errno。
如果你对数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。
结束传输
当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:
close(sockfd);
你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。
int shutdown(int sockfd,int how);
Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式:
·0-------不允许继续接收数据
·1-------不允许继续发送数据
·2-------不允许继续发送和接收数据,
·均为允许则调用close ()
shutdown在操作成功时返回0,在出现错误时返回-1并置相应errno。
面向连接的Socket实例
代码实例中的服务器通过socket连接向客户端发送字符串"Hello, you are connected!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。
该服务器软件代码如下:
#include #include #include #include #include #include #include #include #define SERVPORT 3333 /*服务器监听端口号 */ #define BACKLOG 10 /* 最大同时连接请求数 */ main() { int sockfd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */ struct sockaddr_in my_addr; /* 本机地址信息 */ struct sockaddr_in remote_addr; /* 客户端地址信息 */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket创建出错!"); exit(1); } my_addr.sin_family=AF_INET; my_addr.sin_port=htons(SERVPORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero),8); if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ == -1) { perror("bind出错!"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("listen出错!"); exit(1); } while(1) { sin_size = sizeof(struct sockaddr_in); if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, \ &sin_size)) == -1) { perror("accept出错"); continue; } printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr)); if (!fork()) { /* 子进程代码段 */ if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1) perror("send出错!"); close(client_fd); exit(0); } close(client_fd); } } }
|
服务器的工作流程是这样的:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用listen在相应的socket上监听,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过新的socket向客户端发送字符串"Hello,you are connected!"。最后关闭该socket。
代码实例中的fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。
客户端程序代码如下:
#include #include #include #include #include #include #include #include #define SERVPORT 3333 #define MAXDATASIZE 100 /*每次最大数据传输量 */ main(int argc, char *argv[]){ int sockfd, recvbytes; char buf[MAXDATASIZE]; struct hostent *host; struct sockaddr_in serv_addr; if (argc < 2) { fprintf(stderr,"Please enter the server's hostname!\n"); exit(1); } if((host=gethostbyname(argv[1]))==NULL) { herror("gethostbyname出错!"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket创建出错!"); exit(1); } serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(SERVPORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero),8); if (connect(sockfd, (struct sockaddr *)&serv_addr, \ sizeof(struct sockaddr)) == -1) { perror("connect出错!"); exit(1); } if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) { perror("recv出错!"); exit(1); } buf[recvbytes] = '\0'; printf("Received: %s",buf); close(sockfd); }
|
客户端程序首先通过服务器域名获得服务器的IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket。
函数gethostbyname()是完成域名转换的。由于IP地址难以记忆和读写,所以为了方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数原型为:
struct hostent *gethostbyname(const char *name); 函数返回为hosten的结构类型,它的定义如下: struct hostent { char *h_name; /* 主机的官方域名 */ char **h_aliases; /* 一个以NULL结尾的主机别名数组 */ int h_addrtype; /* 返回的地址类型,在Internet环境下为AF-INET */ int h_length; /* 地址的字节长度 */ char **h_addr_list; /* 一个以0结尾的数组,包含该主机的所有地址*/ }; #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/
|
当 gethostname()调用成功时,返回指向struct hosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,而应该使用herror()函数来输出。
无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。
阻塞和非阻塞
阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞(blocking)。而非阻塞操作则可以立即完成。比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。
#include
#include
……
sockfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK);
……
通过设置socket为非阻塞方式,可以实现"轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,返回值为-1,并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销。Select函数原型为:
int select(int numfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct timeval *timeout);
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:
FD_ZERO(fd_set *set)----清除一个文件描述符集;
FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中;
FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除;
FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。
Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为:
struct timeval {
int tv_sec; /* seconds */
int tv_usec; /* microseconds */ };
POP3客户端实例
下面的代码实例基于POP3的客户协议,与邮件服务器连接并取回指定用户帐号的邮件。与邮件服务器交互的命令存储在字符串数组POPMessage中,程序通过一个do-while循环依次发送这些命令。
#include #include #include #include #include #include #include #include #define POP3SERVPORT 110 #define MAXDATASIZE 4096
main(int argc, char *argv[]){ int sockfd; struct hostent *host; struct sockaddr_in serv_addr; char *POPMessage[]={ "USER userid\r\n", "PASS password\r\n", "STAT\r\n", "LIST\r\n", "RETR 1\r\n", "DELE 1\r\n", "QUIT\r\n", NULL }; int iLength; int iMsg=0; int iEnd=0; char buf[MAXDATASIZE];
if((host=gethostbyname("your.server"))==NULL) { perror("gethostbyname error"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket error"); exit(1); } serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(POP3SERVPORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero),8); if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){ perror("connect error"); exit(1); }
do { send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0); printf("have sent: %s",POPMessage[iMsg]);
iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0); iEnd+=iLength; buf[iEnd]='\0'; printf("received: %s,%d\n",buf,iMsg);
iMsg++; } while (POPMessage[iMsg]);
close(sockfd); }
|
遇到 ORA-27121 错误
ORA-01034: ORACLE not available
ORA-27121: unable to determine size of shared memory segment
Linux Error: 13: Permission denied
主要是因为oracle安装程序没有给oracle这个可执行程序设置正确的setuid。这样设置一下:
$ cd $ORACLE_HOME/bin
$ chmod 6751 oracle
结果类似这样就行了。
$ ls -l $ORACLE_HOME/bin/oracle
-rwsr-s--x 1 oracle oinstall 23352783 Aug 14 2001 /home/oracle/app/oracle/product/8.1.6/bin/oracle
这个错误的典型现象:使用oracle用户一切正常,但是在使用其它用户时,使用sqlplus无法登录,报告上面的错误!
在将SYBASE的Trigger移植到ORACLE的时候发现一个问题,
就是在一张表的Trigger中不能含有操作该基表的SQL,
例如STOCKINFO的Trigger里不有操作STOCKINFO的SQL。
那加权平均价如何取呢?
现在只能在原平均价的基础再与新价进行加权平均,结果是一样的。
注意:
AFTER UPDATE的Trigger不能操作基表,只有在BEFORE INSERT的Trigger里才能操作基表。
BEFORE INSERT的Trigger只有用INSERT INTO T1(COL1) VALUES('1');才有效,
INSERT INTO T1(COL1) SELECT '1' FROM DUAL;也是无效的。
例如:
SQL> insert into t1 values (1,'a'); --先插入一条数据,避免ORA-01403: no data found 错误。
1 row inserted
SQL> commit;
SQL> create or replace trigger tri_t1
2 before insert on t1 for each row
3 declare
4 cvar varchar2(10);
5 begin
6 select 'Y' into cvar from t1 WHERE ROWNUM=1;
7 end;
8 /
Trigger created
SQL> insert into t1 values (2,'b'); -- insert into ... values 没有问题
1 row inserted
SQL> insert into t1 select '3','c' from dual; -- insert into ... select .. from 报错
ORA-04091: table TEST.T1 is mutating, trigger/function may not see it
ORA-06512: at "TEST.TRI_T1", line 4
ORA-04088: error during execution of trigger 'TEST.TRI_T1'
并且:old和:new只有在行级(for each row)的Trigger有效。
网上原文:
ORA-04091与 table mutating
近日解决了一个trigger中报ORA-04091错误的问题,补了关于Oracle table mutating的一课:
mutating table 是指一个当前正在被update,delete,insert语句修改的表,如果在一个行级别的trigger中读取或修改一个mutating table,则往往会遇到ORA-04091错误.例如,如果在trigger中使用了select或dml 语句访问trigger所在的表,则就会收到这个错误。
然而,Oracle8i和9i文档中都没有解释清楚before和after 类型的 row trigger 在对待两种不同的insert语句(insert into ... values ... 与 insert into ... select ...)时的差别:
1、对于after 类型的 for each row 级别的triggers,不论哪种insert语句触发了trigger,都不允许在 trigger 中访问本trigger所依赖的table的,测试如下:
SQL> create table t1 ( c1 number,c2 varchar2(10));
Table created
SQL> create or replace trigger tri_t1
2 after insert on t1 for each row
3 declare
4 cvar varchar2(10);
5 begin
6 select 'Y' into cvar from t1 WHERE ROWNUM=1; --这里访问了trigger 本表
7 end;
8 /
Trigger created
SQL> insert into t1 values (1,'a');
ORA-04091: table TEST.T1 is mutating, trigger/function may not see it
ORA-06512: at "TEST.TRI_T1", line 4
ORA-04088: error during execution of trigger 'TES.TRI_T1'
SQL> insert into t1 select '1','a' from dual;
ORA-04091: table TEST.T1 is mutating, trigger/function may not see it
ORA-06512: at "TEST.TRI_T1", line 4
ORA-04088: error during execution of trigger 'TEST.TRI_T1'
2、对于before 类型的 for each row 级别的triggers,如果使用 insert into ... values 语句触发此trigger ,则在trigger 中访问本table没有问题;
但如果使用 insert into select .. from 语句触发此trigger ,则在trigger 中访问本table就报ora-04091错误;
只有在Oracle 7标准的开发文档中有这样的说明:
From the Application Developers Guide
"There is an exception to this restriction;
For single row INSERTs, constraining tables are mutating for
AFTER row triggers, but not for BEFORE row triggers.
INSERT statements that involve more than 1 row are not considered
single row inserts."
"INSERT INTO <table_name> SELECT ..." are not considered single row
inserts, even if they only result in 1 row being inserted.
测试如下:
SQL> drop trigger tri_t1;
Trigger dropped
SQL> insert into t1 values (1,'a'); --先插入一条数据,避免ORA-01403: no data found 错误。
1 row inserted
SQL> commit;
SQL> create or replace trigger tri_t1
2 before insert on t1 for each row
3 declare
4 cvar varchar2(10);
5 begin
6 select 'Y' into cvar from t1 WHERE ROWNUM=1;
7 end;
8 /
Trigger created
SQL> insert into t1 values (2,'b'); -- insert into ... values 没有问题
1 row inserted
SQL> insert into t1 select '3','c' from dual; -- insert into ... select .. from 报错
ORA-04091: table TEST.T1 is mutating, trigger/function may not see it
ORA-06512: at "TEST.TRI_T1", line 4
ORA-04088: error during execution of trigger 'TEST.TRI_T1'
我们的开发人员因为不知道这个特别情况,近日在修改一个存储过程时候,将原来的insert values 写法改成了insert select 写法,而trigger 又是before类型的,导致出现了ORA-04091错误,搞得分析了好久也没有头绪。
其实,在metalink中有一篇note说到了:
文档 ID: 注释:132569.1
主题: ORA-4091 on BEFORE ROW TRIGGER with INSERT statement
类型: PROBLEM
状态: PUBLISHED
内容类型: TEXT/X-HTML
创建日期: 16-JAN-2001
上次修订日期: 09-AUG-2004
Problem Description
-------------------
You want to do an insert into a table that has a BEFORE row Trigger.
When you hard code the values into the INSERT statement, the trigger works fine.
For example:
INSERT
INTO content (cont_name,cont_seg,cat_seq)
VALUES('blah',100,200);
1 row created.
However, your trigger errors with ERROR ORA-4091 with
INSERT INTO...select statement:
INSERT
INTO content (cont_name,cont_seq,cat_seq) (select....from category);
ERROR at line 1:
ORA-4091: table <schema>.CONTENT is mutating, trigger/function may not see it
ORA-6512: at "<schema>.INS_CONTENT", line 4
ORA-4088: error during execution of trigger '<schema>.INS_CONTENT'
TRIGGER:
CREATE OR REPLACE trigger INS_CONTENT
BEFORE INSERT on CONTENT
FOR EACH ROW
DECLARE
max_sort number;
BEGIN
SELECT max(cont_sort) INTO max_sort FROM CONTENT;
IF max_sort IS NOT NULL AND max_sort!= 99999 THEN
IF :new.cont_sort IS NULL THEN
:new.cont_sort := max_sort +1;
END IF;
END IF;
SELECT SEQ_CONT_SEQ.nextval INTO :new.CONT_SEQ from dual;
END;
Explanation
-----------
Error: ORA 4091
Text: table %s.%s is mutating, trigger/function may not see it
-------------------------------------------------------------------------------
Cause: A trigger (or a user defined PL/SQL function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
Action: Rewrite the trigger (or function) so it does not read that table.
Explanation:
You cannot look at or modify the table that is mutating.
Note:
From the Application Developers Guide
"There is an exception to this restriction;
For single row INSERTs, constraining tables are mutating for
AFTER row triggers, but not for BEFORE row triggers.
INSERT statements that involve more than 1 row are not considered
single row inserts."
"INSERT INTO <table_name> SELECT ..." are not considered single row
inserts, even if they only result in 1 row being inserted.
RELATED DOCUMENTS
-----------------
Oracle Application Developer's Guide (A68003-01)
Chapter 'Using Database Triggers', page 13-22)
搜狐博客 > 江湖一声笑 > 日志 > 经济述评 2007-04-20 | 房地产绑架了中国经济(王超,转) 房地产绑架了中国经济,全国性的泡沫已经形成!!!
标签: 投资 呼和浩特 上海 深圳 房价
经过几年的持续上涨,深圳、上海、杭州、北京、广州等一线城市的房价,已达到“一套房消灭一个百万富翁”的水平。
高速膨胀的地产,不但在不公正挤占社会财富,也挤占了其他产业的发展空间。
价格高的商品房,不但抬高了社会生产、生活成本,削弱了中国在全球的竞争力,也让中国经济变得脆弱,出口依赖度直线上升。
经过三级市场的成功渗透,在一些城市,房产市场形成了“先富起来的人对未富者进行掠夺”的市场格局。
地产绑架了中国经济
十五期间,一些地方政府相继将“地产”列为“支柱产业”的结果,导致这一行业迅速跨越它在产业链中本有的位置,成为中国经济中的一个“异数”。脱离社会实际支付水平的房价和庞大的开发规模,在使“地产行业”不正当占用了过多社会财富的同时,大大削弱了中国居民在其他领域的购买能力,并对其他产业的发展形成强硬挤压。 “2005年,中国经济出口依赖度接近70%”这一数据,实际也在传递这样一个信号:在医疗、教育、尤其是价格居高不下的房产的交替作用下,中国本就不均衡的市场购买力在进一步萎缩,以致不得不加重对国外市场的依赖,中国经济的脆弱性几乎已到了“生死存亡”的危险关头。
可以说,连续5年的“地产高烧”,已给中国经济、社会的可持续发展注入了多种隐患,近年各主要城市由地产开发支撑起来的、看起来让人满意的GDP增长数据,一经细分,完全可以成为这些隐患严重性、广泛性、真实性的证据。而更为严峻的是,作为不动产,房地产实际是一种终端行业,牵涉着数十种上游产业,比如从2004年开始,房地产就消耗了国内差不多一半的水泥和钢材,这意味着,由房地产直接按放在中国经济中的隐形炸弹,除了目前银行已知的近4万亿房贷外,还有数十个相关行业为满足房地产膨胀所带来的硬性需求、扩大产能所进行的数目庞大的基础投资。它的崩溃,不仅仅只关涉到我国的金融安全,同时也关涉到我国的产业安全。而2005、2006两年,国内绝大部分城市房价在国家连续出台的宏观调控政策下,继续强硬上扬的事实,似乎在进一步表明:地产绑架了中国经济。
寻找“汤臣一品”
2006年8月以11万每平方米开盘的上海豪宅“汤臣一品”,在《上海证券报》爆出其地价仅为每平方米4000多元后,被众多网络人士一致推为“中国地产泡沫与暴利”的最佳代表。
但是,另人没想到的是,这个热遍网络、堪称中国最知名的楼盘,竟是这样难找。
据悉,2007年1月7日,在上海外国语学院附近,一记者连续拦了5辆出租车,告知“去汤臣一品”,5位司机均一脸茫然,即使记者一再解释这个小区“坐落在陆家嘴,距金茂不远、是中国最昂贵的楼盘”,这些本该对上海地理滚瓜烂熟的司机还是大摇其头,表示“从没听说过,不知在哪里”,无奈之下,记者只好请出租司机先到陆家嘴,边转悠边问。
进入陆家嘴地盘,出租司机连续三次下车,用本地话向路边的卖店、饭店打听“汤臣一品”在哪里?都失望而归,在漫无目的地搜寻了几条街后,司机才在一座快要竣工的写字楼前,从一个负责施工的技术员那里打听到了汤臣一品的确切位置。原来这座声名在外的“中国最昂贵的楼盘”,在距金茂大约一站地的花园桥西路和拾步街之间。
在“汤臣一品”小区的围墙外,这个年近50岁的司机在眯着眼仔细看了一番矗立在车窗外的“中国最昂贵的楼盘”后,突然没有节制地大笑起来,边笑边摇头,嘴里还不住叨咕:“汤臣一品,每平方米11万”,每叨咕一遍就笑得更厉害,他瞅着汤臣一品装饰豪华却空空荡荡的售楼中心,最后竟笑趴在了方向盘上。出租司机的突然失态,让约10米外两个本来笔直站着的保安变得非常尴尬,讪笑着望着我们。
过了约3分钟,出租司机才慢慢恢复常态,他向记者表示歉意,说自己突然莫名其妙想起了《大腕》里那个说起话来滔滔不绝的神经病。
汤臣一品所在的区域,被称为小陆家嘴,这个面积仅1.7平方公里的弹丸之地,密布着中国最昂贵的豪宅,半年前,一个当地记者计算出,按照当时该区域的平均房价,仅在1/10的地面建造40层的高楼,其市值就与上海股票交易所的全部股票市值相当。“任何一幢楼都抵得上几家上市公司的流通市值”。
一业内人士告诉记者,虽然自己并不看好汤臣一品的销售前景,但却无法忽视这个楼盘为上海乃至中国地产带来的强烈象征意味。
“每平方米11万”所传播的地产价值暗示,在为上海现有高价楼盘的价格敏感度松了绑的同时,可以拉高周边、上海其他区域乃至全国多少高档楼宇的售价?同时他认为,汤臣一品在国家连续三年、一年比一年严厉的宏观调控政策下横空出世,是目前中国地产有些失控的最好注脚。
虽然,上海号称最近8个月商品房价格指数在连续下跌,但实际上,上海内环的大多数一手房、二手房依然处于微升状态,2006年,上海商品房总竣工约3000万平方米,而同期销售仅1000万平米出头,当年积压近2000万平米已成定局。
由于民间舆论和宏观调控带来的双重压力,从2005年上半年开始,上海调整了自己对地产的宣传定位,开发商与消费者随之进入明显相峙状态,但国内其他城市却迅速接过了上海手里的接力棒。首先是深圳,从2005年9月份开始,当地房价以每月5%左右的速度一路狂涨,有的月份上涨幅度甚至突破10%,到2006年初,深圳关内已经鲜有一万以下的一手房,二手房价格也飙高到8000元每平方,随后,北京开始发力,2006年6、7、8、9月,北京房价同比分别上升11.2%、11.1%、11.4%和10.3%,不断“领跑”的结果是,至冬季房展,北京五环内一万以下的一手房几乎绝迹,三环周边的二手房均价追至8000,12月底,这些地域的二手房也突破万元大关。
广州、南京、福州、武汉、成都、沈阳、哈尔滨、乌鲁木齐、西安、大连、青岛、宁波、苏州……国内几乎所有区域经济中心城市,都跟在北京、深圳这两部经济引擎后面,价格一路追高;这样的氛围,甚至让公认为是中国地产过热的重灾区杭州,在2006年也开始蠢蠢欲动,虽然早在2004年,就有人指责“杭州开发商囤积的土地,足够杭州开发五至十年”,但2006年初,当地媒体用“全线飘红”来形容了政府当年举行的第一次土地拍卖;这表明,导致杭州房价上涨的市场机制并没有改变,由地方政府、开发商及其附属利益团体组成的“铁三角”,依然把控着当地市场的节奏和走向。
2006年3月,一些与地产界关系密切的上海本土专家,以学者身份四处接受采访,宣称上海地产观望期已经结束;国内包括上海当地不同地段间的区域差异是一种客观存在,这种差异决定了上海黄金地段的高档楼盘还有上涨空间;在市场经济环境下,投机是一种正常的经济行为,可以拉动产业的快速整合,打压这种行为有违市场经济规则;政府不应过多插手上海地产问题,而应交由市场去自行调节。
6月,呼和浩特市政府悍然反击新华社关于“政府这只有形的手在拉高当地房价”的新闻报道,将其拔高到影响“民族和谐”这一政治高度,强调呼和浩特的地产开发完全是“按照中央核准的规则在进行”,呼和浩特的房价“是本地经济和城市化快速发展的结果”。呼和浩特政府从“2004年至今,投资于经济适用房的投资占整个房产开发投资的54%”,所以“呼和浩特商品房的结构也是健康的”。不过,5个月后,由该市房产局提供的“2006年1-10月呼和浩特房地产市场形势分析”却显示,呼和浩特这一时间段的“经济适用房施工面积占住宅施工面积的比重为8.72%”。且这一比例不但比去年同期“提高了1.13个百分点”;而且环比“也呈现较明显的提高”;统计显示,“发展健康”的呼和浩特地产开发市场,商品房均价已从“2004年的1590元每平方窜至2006年的3481元每平方”,短短两年升幅高达218.89%。同时,与高达3481元的均价形成鲜明对比的是,2006年1-10月该市的土地购置费每平方米仅为509元,2005年更低至每平米374元。
2006年1-10月,呼和浩特商品房竣工率仅13.02%,去年同期更低,为11.23%,不到全国平均竣工率的1/3,这意味着:呼和浩特房地产开发商囤积土地的迹象非常明显。与这一低竣工率对比鲜明的是,呼和浩特住宅销售速度远高于同期的竣工率,1-10月, “呼和浩特共竣工商业住宅竣工58.69万平米,销售76.98万平米”,实际销售率出现了国内其它城市罕见的一个数字:131.16%,其中仅在10月份,呼和浩特商品房预售面积就达13.43万平米,比去年同期狂增206.6%,有专家认为,联系它超低的竣工率来看,这一反常比例,除可能有呼和浩特市政府姿态强硬的声明引起市场追风效应外,也不排除“开发商在人为控制市场供应量”。
房价上涨之源
究竟是什么因素导致了最近几年中国城市房价的持续上涨?
在众多关于房地产上涨因素的分析中,几乎没有人注意到深圳于2001年3月26日开始正式实施的《深圳市政府100号令》,事实上,由该法令所确立起来的国内地产市场的运作原则和模式,与最近几年全国范围房价一路追高有着极深的渊源。
由于内容主要是由“当地土地主管部门在亲自操刀”,加上具体参与者对“市场经济的复杂性知之甚少”,过于迷信香港经验,导致《深圳市政府100号令》这一有“国内第一个规范地产市场运作规则”之称、本意为“防止划拨、协议转让土地过程发生腐败”的地方法规,在客观上帮助主管部门完成了对土地和房产开发的统控,并否定了其他市场主体的功能和作用,强行终止了此前存在于商品住宅市场的多样化特征,将满足居民住宅需求路径高度统一在了“以商品房为主体的市场模式上”。
2001年前,城镇居民的住宅解决途径有《城镇个人建造住宅管理办法》、《国务院关于进一步深化城镇住房制度改革、加快住房建设的通知》两个法规可依,这两个法规一个在“鼓励城镇个人建造住宅”,另一个则明确要求各地“建立和完善以经济适用住房为主的住房供应体系”,也就是说。各地商品住宅的市场主体应为“经济适用房”。
各地依据这两个法规形成的市场体系,让当时的城镇居民至少可以通过以下几种途径满足住房需求:集资建房、合作建房、经济适用房和纯粹的商品房。
但是,“100号令”通过确立“土地局是土地交易唯一合法的部门”,将土地交易、包括第二次、第三次土地交易大权,收归一个部门统控,要求深圳所有土地及其地上附属物的交易,都必须由土地局直属机构----深圳市土地交易中心以“拍卖为主、挂卖为辅”的方式 “公开、公正、公平”进行,这些规定,在符合了形式上的规范与透明外,也衍生出了“地产市场功能单一、难以承载城市多样化的用房需求,土地局职能从此前以管理和监督为主变成了坐庄城市地产开发的‘土地爷’、可以轻易调控房产开发的规模与节奏、权力一枝独大,政府与开发商易形成市场同盟、直接与民争利”等弊端;同时,由于合作建房、集资建房的操作路径与这一法规所确立的交易规则无法兼容,导致商品房市场的多元化特征被迫实际终止,加上本应形成制约关系的房产、土地、规划、甚至勘探部门,在深圳全部归口在土地局,这无疑进一步强化了土地局对整个市场的集权程度。有学者一针见血地指出,这一法规实质具有这样的含义:(深圳)政府把老百姓的住房利益整体出售给了开发商。
基于当地各界对《深圳市政府100号令》意见极大,当时在新华社广东分社就职的记者曾于2001年9月,三次前往深圳进行专题调研,得到的情况反馈是:为了反击社会负面评价,巩固这一政令,深圳土地局不惜耗费巨资、以每月支付10多万宣传费为代价,邀请国内外知名媒体前来为它“唱赞歌”,同时还广邀其他省市的同行前来观摩“这一经验”,试图通过“以最快的速度在国内推广”的方式,让其成为既成事实。记者曾在深圳土地交易大厅的贵宾记事本上看到,沈阳、山西、安徽、河南等地的土地管理部门官员,已在那个豪华的记事本上留下了不少溢美之辞,此外“还有不少省市的主管部门官员也将陆续到深圳土地局来考察”。
记者在随后形成的内参报告中,曾明确担忧“这一法规实际终止了此前地产市场其他市场主体的合法地位,导致市场发育严重单极化,重新回到垄断状态,实质是市场经济的一个退步,并将给房产市场带来不可预知的发展隐患”。
不过,深圳市的“新举措”,却让一些财力匮乏、当地房地产市场又启动艰难的地方政府如获至宝。迅速按这一口径调整了本地房地产市场的结构与运作模式:土地房产局坐庄房产开发市场,有购买需求的城市居民则几乎全部被驱赶进了商品房市场。
效果立竿见影。
以沈阳为例,2001年,沈阳房地产营业额约55亿,亏损2亿余元,这一年,整个辽宁省的土地转让收入仅为1.6亿元。但从2002年实行“土地集中拍卖”政策后,沈阳地方财政来自房地产收入直线上升,当年,沈阳地方财政近1/3来自土地出让金和房地产税收。牛刀小试尝到甜头后,沈阳——这个中国最重要的重型机械制造基地,索性将房地产列为了该市的支柱产业,2003年,它把超过1/3的固定资产投资投向了地产,同时,约1/3的地方财政收入也来自房地产开发,2005年,在遭遇宏观调控之后,沈阳地产投资依然保持了超过20%的增长速度,仅收取的土地出让金和配套费用就高达42亿。
由于这一模式下的房地产开发能快速拉高当地GDP、并迅速改善地方政府的财政状况,相当部分地方政府以前所未有的热忱投入到了房地产大开发中,“以经济建设为中心”被实际压缩成了“以房产开发为中心”。同时,这一标榜“公开、公平、公正”的市场模式,并没有遏制住房地产领域的腐败,反而让腐败愈演愈烈,据知名旅美学者徐滇庆介绍,经不完全统计,最近几年,中国主要城市流通在房地产市场的资金,有高达1000多亿不知去向。
在市场经济里“后知后觉”的呼和浩特,由于开始阶段没有跟风,直到2004年当地商品房均价才1590元/米,看到其他兄弟城市干得风生水起,呼和浩特失落之余,终于琢磨出了这其中简单的道理,于是2004年该市也如法炮制,将“土地收储中心”调整为政府直属机构,开始通过统一的“挂拍卖”坐庄房产开发市场,果然,远在内蒙古大草原的呼和浩特,房产市场也顿时风生水起,2005年,该市商品房均价达到2500元左右,2006年,均价接近3500元。在呼和浩特此轮涨价旋风中,政府每年拿到了几亿的小头,开发商拿到了十几亿的大头,当地老百姓则得为“这一规范的市场运作机制”多掏十几亿房款。
一组可怕的数据
在一个国家的产业链条中,房地产究竟应该处在一个什么样的位置?
上海交通大学经济管理学院孟宪忠教授表示:在一个经济体系的产业链中,房地产既是一种基础性产业,也是一个对其它产业的发展具有严重依赖性的产业。后一个角度,表示房地产市场与其他经济活动所创造的财富值之间,存在一个对等的比值关系。通俗地说,就是一个社会创造财富的多少,决定它房地产市场的繁荣程度和房价高低。
综合世界各国的情况看,一个健康的房地产市场,其在社会总投资中所占的份额应在10%左右,超过这个份额就会出现投资过热,它带来的负面影响是降低社会资源配置的合理程度,引起社会不必要的财富损耗,甚至成为社会财富的杀手,并损害产业间的平衡协调。从经济健康和可持续的角度,商品房价格与当地家庭年均收入之间,也有一个比值,这个比值根据不同国情,在1:3至1:6之间,超过这一比值,房地产就不但在不正当剥夺购买者的财富,也会从整体上削弱社会对其他产品的购买能力,给其他产业的发展带来障碍。
按照这一判断标准,最近几年,国内主要城市的经济发展数据,传达出的是一个社会资源长期配置错位、明显存在严重经济隐患的信息——
浙江经济圈的中心城市杭州,2003年社会固定资产投资1006.18亿元,其中房地产开发328.54亿元,占全部固定资产投资的32.6%,全市财政总收入达329.71亿元,土地出让所得109.29亿元;2004年社会固定资产投资1205.18亿元,其中房地产开发企业完成投资328.54亿元,占当年社会固定资产投资的27.26%。2005年,该市房地产开发投资更上层楼,达到410.57亿元。
珠三角重镇广州,2003年完成全社会固定资产投资1157.77亿元,其中房地产开发投资405.94亿元,占35.1%。2004年完成全社会固定资产投资1321.96亿元,其中房地产开发投资440.81亿元,占33.3%,2005年完成社会固定资产投资1445.33亿元,其中房地产开发投资488.86亿元,占33.7%。
华东六省一市的经济龙头上海,2003年完成固定资产投资2452.11亿元,其中房地产开发投资901.24亿元,比重为36.8%。2004年完成全社会固定资产投资3084.66亿元,其中房地产开发投资1175.46亿元。比重为38.1%,2005年完成全社会固定资产投资总额3542.55亿元,其中房地产开发投资1246.86亿元,比重为35.2%。
中国改革的窗口深圳,2003年完成全社会固定资产投资额946.49亿元,其中房地产开发投资410.06亿元,占43.4%;2004年全社会固定资产投资额1090.14亿元,其中房地产开发投资432.01亿元,占39.6%,全年完成地方财政一般预算收入321.75亿元,国土基金总收入128.46亿元,2005年完成全社会固定资产投资1176.13亿元,其中房地产开发投资418.76亿元,占33.%。
中国政治中心、中国北方重要的经济中心城市北京,2003年完成全社会固定资产投资2157.1亿元,其中房地产开发投资1202.5亿元,比重55.7%,且仅比上一年多了1.2个百分点。2004年,北京全社会固定资产投资2528.3亿元,其中房地产开发投资1473.3亿元,比重为58.2%,2005年完成全社会固定资产投资2827.2亿元,其中房地产开发投资1525亿元,比重为53.9%。
中国北方重工制造中心沈阳,2003年完成全社会固定资产投资582.6亿元,其中房地产开发投资177.3亿元,比重为30.1%;2004年全社会固定资产投资971.4亿元,其中房地产开发完成投资342.6亿元,比重为35.2%,2005年固定资产投资完成1363.2亿元,其中房地产完成投资413.6亿元,比重为30.3%。
中国经济大省省会南京,2003年完成全社会固定资产投资额954.04亿元,其中房地产开发投资183.80亿元,比重为19%;2004年完成全社会固定资产投资额1201.88亿元,其中房地产开发投资292.88亿元,比重为24.3%,2005年完成全社会固定资产投资额1402.72亿元,其中房地产开发完成投资额296.14亿元,比重为21.1%。
华中枢纽武汉,2003年全社会固定资产投资完成645.06亿元,其中房地产投资169.55亿元,比重为26.2%,2004年全社会固定资产投资完成822.20亿元,其中房地产投资233.30亿元,比重为28.3%,2005年全市完成全社会固定资产投资1055.18亿元,其中房地产开发投资297.99亿元,比重为28.2%。
这些城市,都是中国最重要的经济发动机或区域经济中心,但最近5年来,这些发动机都把自己的经济引擎按放在了“不动产”上,将超过30%的全社会固定资产投资投向了房地产,有的这一比例更高达50%,远远高于10%的正常水平。由于房地产的强力拉动,它所必然引起的投资过热也相应凸现,虽然从2002年开始,国家宏观经济部门就开始不断警告我国经济出现了投资过热,但依然无法阻挡这股气势凶猛的洪流,2003年,我国全社会固定资产投资增26.7% ,2004年第一季度,全社会投资突然增速,同比增长高达43%,不但比上年同期加快了15.2个百分点,也远高于1992和1993年我国经济过热时全社会固定资产投资所创下的29.1%和35.2%这一增速;2004年全年,我国全社会固定资产投资达70073亿元,增25.8%;2005年我国全社会固定资产投资88604亿元,增25.7%;2006年上半年,全国固定资产投资较去年同期增31.3%。此外,我国在建项目规模2005年为27万亿元,2006年底则飙高至32万亿元。
一从事宏观经济研究的专家告诉记者:很难想象各地政府为什么会进行这样的投资分配,因为这给整个经济结构带来的破坏和隐患极其明显,房地产膨胀所带来的“真实而强劲”的市场需求,必然带动与其相关的数十个产业的膨胀,所以房地产热一定会引发整个社会投资的过热。
他表示,地方政府通过整合强大的政策和市场资源,可以在短时间内维持房地产市场份额在整个社会产值中占到一个较高比例,但这样的高比例绝不可能长期持续,这是由房地产在产业链中本有的位置所决定的,于是问题出现了-------由房地产拉高的上游相关产业的产能很快就将被悬空,如果这个悬空的程度大到了产业自身无力消解,就会发生我们担心的经济萧条甚至产业崩溃。
宏观调控发生后,一些学者从房地产拉动了多少份额的产品消费,以此证明房地产对GDP的重要贡献,完全可以从另一个层面求得房地产膨胀导致的产业隐患规模。
“最近几年,各地政府对GDP的理解存在一个误区,就是只重数字增长却不分析经济行为的合理性,事实上,GDP的参考价值与被分析对象行为的理性程度是成正比的,经济行为愈理性,GDP的参考价值愈高,反之则愈低,所以,针对目前各地赞歌不断的GDP,可能我们最该去关注的是,这些GDP背后的经济行为的理性程度究竟有多高?现在中央倡导的科学发展观,不仅仅要求各地不得以‘经济发展’替代‘社会发展’,而且也内含着提高决策、资源配置、经济行为理性的诉求”。
一场危险的游戏
除投资规模过于庞大外,房地产另一个问题更为突出——主要城市的商品房价格已远高于社会实际购买能力。
即使最坚定支持“房地产价格应由市场去调节”的人也会同意,房价与所在地居民收入之间,应该存在一个合理的系数,这个系数,联合国人居中心的上限是3:1,世界银行的上限是5:1,意思是“一个拥有当地平均收入水平的家庭,用3-5年的收入总和,可以购买到所在地平均价格、平均居住面积的住房”。
按照这个标准,中国几乎所有主要城市的房价,已连续数年严重超过这一标准。其中,“中国著名的经济中心长三角的各大城市,平均房价收入比超过理性房价收入比的1.93倍,杭州、上海、南京、宁波的比例更超过2倍甚至达到3倍的比例”。国务院发展研究中心一位部门负责人对记者坦陈:按照目前的房价,他是买不起房子了。
2001年,中国房地产被外界形容为“出现局部过热”时,全国房价只有深圳、杭州、温州商品房均价超过了5000元/米,广州、北京接近5000元/米,上海商品住宅均价3413元/米,内环边5年左右房龄的二手房价格保持在2500元左右,全国35个纳入物价指数统计的大中城市中,厦门、杭州、南京、沈阳、大连、天津、哈尔滨、昆明、福州、海口等地商品房价格在2000-3000元/平方米之间;其他21个城市商品房价格在2000元/平方米以下。到2006年底,这些城市的商品房均价几乎都翻了一倍。深圳、上海、杭州、北京、广州的房价,已达到“一套房消灭一个百万富翁”的水平。
2006年底,由中国金融网发起的“中国城市房价究竟高了多少”的网络调查,截至2007年1月6日,有11%的网友认为高了30%,24% 认为高了30-50%,65%认为高了50%以上。
有学者认为,这一调查数据显示出的结果与现实情况比较吻合,他说,即使按照世界银行公布的家庭收入与房价5:1这个上限来看,经过最近几年的涨价风潮,我国绝大部分一、二线城市的房价已远远超标,比如上海,2005年其人均可支配收入为18645元,其家庭人均人口3.1人,年总收入为57799.5元,5年总收入为288997.5万,这个数据体现的是当地实际发展水平和实际购买力,当年上海人均建筑面积达33.07平方米,按照这个实际购买力和当地人均居住面积之间形成的系数,它的住房均价应该为4171元/米,与它2002年上半年平均房价相当,但实际上,这个数据同期变为了8627元;深圳2005年人均可支配收入21494元,按照当时全国城镇人均住房系数,它的住房均价应该为4578元/米,但深圳2001年的商品房均价就高出这个数字近千元,同期实际均价为6952元 ;北京2005年人均可支配收入17653元,理性房价上限为3760元,但实际为6232元;广州2005年人均可支配收入18287元;理性均价上限为3895元,但实际为5660元;南京2005年人均可支配收入14997元,理性均价上限为3194元,但实际为4960元;杭州2005年人均可支配收入16601元,理性均价上限为3536元,但实际为7029元; 青岛2005年人均可支配收入12920元,理性均价上限为2751元,但实际为4639元;成都2005年人均可支配收入11359元,理性均价上限为2419元,但五城区实际为4085元;武汉2005年人均可支配收入10850元,理性均价上限为2311元,但实际为3300元;沈阳2005年人均可支配收入10098元,理性均价上限为2151元,但实际为3330元;呼和浩特2005年人均可支配收入12150元,理性均价上限为2588元,但实际为3481元。
他说,这其实意味着,我国大部分城市在进行一场危险的游戏,这场游戏以高昂的房地产为核心,在全面拉高中国固定资产投资的同时,通过抬升城市办公、居住、生活成本,大幅增加企业的运转经费,以行业暴利的方式追涨企业成本。导致我们不惜以牺牲环境、一线工人合理工资收益和合理税收等途径积攒起来的成本方面的竞争优势,正在被这一行业暴利全部甚至超额吞噬。从而瓦解我们在全球范围的竞争优势。
这一态势已得到一些国际机构的调查印证,2006年9月26日,总部设在瑞士日内瓦的世界经济论坛公布的《2006~2007年全球竞争力报告》就显示出,2006年,中国的竞争力继续下跌,相比2005年再后延了6个位次,已落后印度11个位次。
在这样严峻形势下,如果依然以“稳定房价”为指导思想来进行宏观调控,这场游戏完全可能“以房产暴利开始,以产业崩溃结束”。因为目前的房价已不应该得到“该去稳定”的结论支持,这个观点不是从一种市场博弈角度给出的,而是依照目前社会实际购买力进行比较衡量的结果。
房地产不但在吞噬城市竞争力,也在吞噬购房者的生活。
2006年初,来自《半岛晨报》的调查表明,由于超高的房价,在大连,有4成购房者感到生活质量明显下降,有3%的购房者感到生活质量严重下降,而几乎就在这同一时间段,一篇名为《高房价导致中国90%白领消失》的网络文章,在不少论坛成为一个被竞相关注的热点,虽然该文所罗列的数据不一定全部真实,但它提出的“(相当部分)购房白领被房产套牢”的观点,被随后《中国青年报》公开的一份统计资料证实,该资料显示,我国贷款买房的群体中,有54.1%月供占其收入的20%~50%,31.8%月供占到了其收入的50%以上。而在国际上,月收入的1/3是房贷按揭的一条被公认的警戒线。美国银行就明确规定,每月偿还的按揭贷款以及与住房相关的税费,不得超过贷款人税前收入的28%。
针对这一问题,知名房产问题专家章林晓曾撰文警告:根据联合国《国民经济核算统计年鉴》和各国统计年鉴,以美、英、法、日为对象,分析它们GDP达1000美元时居民消费结构可以发现,其住房消费支出比例最大的英国为13.8%,最小的法国为8.3%,均低于我国同期水平数倍。他据此认为,这意味着,房地产业在不正当剥夺整个社会的财富。而“这破坏了社会的财富配置,既进一步扩大了社会的贫富分化,给社会带来了不安定,也不利于其他产业的发展,因为社会总体购买力被这个必需品过多地挤占了”。
谁在分摊高房价
在国内一些一线热点城市,经过一、二、三级市场整合后,当地高昂的房价正在从主要由购房者分摊,逐渐变成由全社会在分摊。
这一分摊的路径主要有两种,一是通过提高三级市场的房租,将高昂的房价直接转给租房者,二是转化为各类企业、尤其是服务性企业的经营成本,再由这些企业通过提高服务或销售价格转嫁给社会。
有专家认为,这种转嫁因素,或者说商品房的租售比,实际是深圳、北京两个热点城市2005年底以来房价持续上涨最重要的支撑点。
在深圳关内,一个3-5年房龄、面积100平米的小区商品房,可以轻松租到3500-4000元/月,按一年出租10个月计,年收益在3.5至4万元之间,即使按一万元/米购入,这一投资的年收益也在3.5%-4%之间,如果再加上保值预期,它的投资收益要大大高于同期发放的长期国债。
而据记者所知,中国政府2006年所发放的1950亿元长期国债,在全国范围都受到了疯狂追捧——
上海,市民为买国债很早就在网点前排起了长队,中行位于徐家汇和陆家嘴地的支行,营业头一个小时就售出国债近400万元,农行在对单笔大额做出限制后,竟然在短短半小时就把国债全部售光;重庆,短短几个小时,一亿多国债就被抢购一空;昆明,商业银行门口购买国债的市民排起了长队。几小时内其所承销的2.4亿国债就全部脱手;南京,市民“手拿油条抢购国债”,商业银行“只花了10多分钟,就卖完了所承销的全部1.5亿元国债”,兴业银行也在十多分钟内,在全国卖完了1.5亿的国债配额;合肥,各银行承销网点都出现了排队抢购场面。首发当日上午10时许,大部分银行承销的国债就已售空。甚至芜湖、大庆、苏州这些二三线城市,国债也都出现了空前的热销。
“这些国债,3年期票面年利率3.14%,5年期国债票面年利率3.49%,均低于投资深圳二手房的所得收益”,该专家问记者:“如果你手头有足够的钱,又找不到其他投资途径,给你长期国债和投资深圳的房子赚取租金收益两个投资途径,你会怎么选择”?
北京、上海的情况与深圳类似,在北京,三环附近一间普通的、面积在65米左右的两房一厅小区房,2005年年中的月租金在2300元左右,2006年涨到了2500元,10个月租金收益在2.3万-2.5万。按1万/米计算,年收益在3.5%至3.8%之间。也高于热销全国的长期国债。
上海,内环边45平米左右的一房一厅,租金价格一般在1500-1700之间,10个月资金收益1.5万-1.7万,按目前8000元/平方米的价位,回报在4.17%-4.71%之间。二房的租金收益,也大体与此相当。
于是,从房地产本身这个角度来判断,上海2003-2005年、北京深圳2005-2006年房价涨幅之快,价格之高,虽令人乍舌,但与国内其他一些房价明显缺乏投资支撑的城市比,却居然有了最充足也最合理的理由,毕竟二手房已把这一收益变实,而既然3-5年、5-10年的二手房都有这样的价格支撑,那么一手房均价过万是再正常不过。这同时意味着,在深圳、北京、上海这样的城市,高房价已被成功转嫁给了租房者,实际也就是转嫁给了社会来分担。
在高价位出手的购房者的投资心态,也从实际的购买群体得到了印证,据《深圳特区报》报道,2006年,深圳的购房者有“50%不是自住,全额付款的购房者比例也超过了一半”,投资目的相当明显。
这一状况引起了不少学者的忧虑,有的甚至提出了这样一个激进的观点:高房价通过三级市场的成功渗透,让这场地产高烧变成了“先富起来的人对未富起来的人的一种掠夺”。他说,市场运作必须考虑公平原则,只有这样经济才健康并可持续,即使是新自由主义经济学大师哈耶克,晚年也在花很多时间研究“自由市场道德原则的缺失问题”,并承认这是新自由主义经济学的一块硬伤,自己一直没能令人信服地从理论上找到解决依据。现在这个通过“市场自由调整”而出现的“由全社会来分摊高房价”的局面,从技术层面无可指责,但从它必然引起的不良后果这一角度权衡,这一局面必须扭转,如果不扭转,把房价拉回到正常水平无疑痴人说梦,因为很简单,这是一个导致深圳、北京等一线城市高房价平稳着陆的一块实地,而这个实地的支撑点则是这些城市相较国内其他城市的更高的竞争力,这个高租金则在“毫无创造地消耗这个竞争力”,并支撑这些城市房价继续上扬。
而另一方面,“全国现在有近8万亿存款掌握在20%的人手上,只要这一模式持续,并被愈来愈多的高收入者接受和认同,就会有愈来愈多的人参与进这一模式,一些学者提出的‘让有钱人买了房子租给无钱买房的人居住’,在这一模式下就变成了不折不扣的‘先富起来的人对后富起来的人的掠夺’”。
在谈到城市房价与可持续发展的关系时,有人打了一个“沙漠中驮金子”的比喻:两个人穿行沙漠时发现了金子,一个贪心拿了自己拿不动的分量,最后渴死在了沙漠上,一个很理智地拿了一点点,最后终于走出沙漠并致了富。
“房产开发获利”,可以看作是“金子”,这个“金子”对于处于发展进程中的城市(穿越沙漠的人)而言,是真实的,但对这个“真实的金子”,城市管理者的理智态度应该是“按照自己能轻松承载的能力”(即按城市可支配收入形成的购买力)去拿,这样才能顺路地一路走下去,否则“会被这个真实的宝物最终压塌”。
四川联合大学南亚研究所欧东明副教授表示,如果建立了理性的产业发展判断,即使“房产开发既得利益集团”也应该去支持健康的房价,因为这才是行业可持续的根本,最近几年一些城市客观上存在的对宏观调控的抵触,一定程度在于这些城市的管理者和房产开发既得利益者对产业的平衡与可持续之间的关系认识不足,假如他们通过量性分析得知再这样走下去,不出几年等着他们的就将是整个经济体系的绝对萧条甚至崩溃,也许他们会重新考虑自己的整个决策。
他说,在这场地产高烧中,最被误导的其实还是那些拥有自己住房的普通居民,因为从资产来看,他们是绝对地增值了,这无法不高兴。但另一方面,对大多数家庭而言,房子除了满足居住外别无他用,房产增值除了给了他们一个数字财富外,同时还需要他们去分摊必然高涨的生活支出和服务费用,此外他们还要承担未来可能的产业崩溃或经济萧条。也就是说,这个对他们没多大实际价值的数字财富,实际大大增加了他们的生活成本和未来风险。
国内经济已严重受损
持续数年的高房价给中国经济带来的严重隐患,正一一浮出水面。
除直接增加了购房者的支付成本,拉高了中国全社会固定资产投资、使中国经济始终处于投资过热状态外,过多社会财富流向这一生活必需品,以及由此引发的消费预期,使中国本就不均衡的内需进一步不足。最近几年,中国每年的外贸依存度都以10%左右的速度持续上升,2005年达到80%左右,这引起了不少学者的忧虑——中国经济在结构上已非常脆弱,而另一方面,世界各市场的贸易保护主义色彩却越来越浓,这将使中国经济遭遇不测的可能性大增。
外贸依存度体现的是一个国家进出口贸易总额在本国国内生产总值(GDP)中所占的比重,它反映的是一个国家经济对外贸的依赖程度和参与国际分工的程度。从1980年到2001年,美国、日本、德国这些发达市场经济国家,外贸依存度大体稳定在14%~20%的范围内。
但同一时期,在产业结构失衡、第一次分配严重不公、社会保障不足、经济策略上过度依重国外市场等原因的交替作用下,中国的外贸依存度一路升高,2001年后,由于住房因素进一步拉低了社会消费能力,中国的外贸依存度更陡然提速,每年以10%左右的速度蹿升。当年突破50%大关,2003年达到60.2%,2004年超过70%。2005年,中国全年进出口总额超过1.4万亿美元,达到1978年的68倍左右,2006年1~10月份,中国外贸进出口总值达14249.5亿美元,同比增长24.1%,已超过2005年全年水平。
有学者表示,从好的方面看,外贸依存度体现了中国经济的开放程度,表明中国参与全球一体化进程加速,中国经济与世界经济已经形成了相互依赖的伙伴关系;从不好的方面看,这其实是国内市场需求不旺的一种必然选择,也就是说,中国每年的高增长,很大程度上是建立在对外依赖的基础上的,这一模式在相当长的时期掩盖了中国经济存在的问题。
比如,即使在社会围绕房地产争论最激烈的时候,我们也没有达成这样一个共识:房地产“抽血”式的聚集社会财富,将强力挤压其他产业的市场空间,其结果是中国经济整体走上了一条不归路。这里面的道理很简单-----假定社会总财富值是100,正常情况下我们拿20%支付住房费用,30%用于储蓄,50%用于其他消费,但现在住房占用了过多的财富,综合值达到了40%甚至45%,由于这是一种必须的消费,所以它必然挤压消费者用于其他领域的消费数额,于是其他行业分到的社会财富值就会大大缩水。购房的人由于财富值已经入套,他得修改自己的消费计划和比例,没购房的人呢?如果他需要买房,也不得不按现行的房价标准修改自己的消费计划。而购房一族,又是一个社会消费力最旺盛的群体,损害了这个群体的购买力无疑意味着内需市场必然遭遇大幅度缩水。
由于房价过于畸形,国内市场的缩水规模可以说达到了骇人听闻的程度,以上海为例,按照世界银行认同的房价比上限5:1和上海这一时期由可支配收入形成的购买力计算,上海商品房均价应该在4000元出头,但它的实际均价却是8000多元,二者相差4000元以上,按4000元计,2004-2005年,上海大约销售了4000万平米的商品房,这是什么意思呢?等于说两年中,仅上海房产市场就不正当地从购楼者那里多拿走了约1600亿元。
“一个上海就挤占了这么庞大的数额,全国加起来是个什么数字?所以我不得不用‘骇人听闻’这四个字来形容房地产对整个社会的掠夺,而这也必然导致国内其他市场出现严重的需求不足,影响经济的良性与正常”。
来自于央行的最新统计数据,证明该学者所言非虚,2006年11月23日上午,中国人民银行副行长苏宁在接受一媒体采访时透露,我国最终消费占GDP的比重,已从上世纪80年代超过62%下降到2005年的52.1%,居民消费率也从1991年的48.8%下降到2005年的38.2%,二者“均达历史最低水平”,居民储蓄率却上升了9%。
而同一时期,世界平均消费率达78%—79%。是我国的2倍以上的水平。
有“华东六省一市经济发展火车头”之谓的上海,可能已率先感受到了由过分偏重房地产带来的寒意,2005年底,当地社科院在一份年终报告里坦露:2005年1~9月,上海(经济)出现了比较明显的收缩,主要经济指标增速不仅低于2004年同期,而且低于全国同期平均水平,出现了(上海罕见)的“双低”现象。与此同时,上海的工业增长速度也开始大幅度回落,效益下滑,2005年1~9月份,上海工业增长速度为12.4%,低了全国同期增速3.9个百分点。
此外,显示上海经济发达程度和结构合理性的第三产业比重,最近几年更是持续下降,上海第三产业的比重,2002年达到历史最高,为51%,伴随着房价上升。这一比例开始下滑,2003年为 48.4%,2004年为 47.3% ,2005更低至45.6%。
这一情况,近几年在另一个高房价城市-------深圳也类似存在。
三板斧砍掉地产暴利
基于上涨过快的房贷和开始在全国蔓延的炒房之风,2003年10月,央行正式祭出“121号文”,针对房地产发出了收紧银根的信号,开始了本轮宏观调控。
但决策者很快发现,与1993年那次“一控就灵”不同的是,这一次,这个带有预警性的手段,一出手却如入泥潭,不但悄无声息,甚至还引起了强硬反弹:房地产开发资金规模在全社会投资总额中继续高比例领跑,部分地方政府开始在一些公开场合扬言本地房地产发展健康,不存在泡沫。
宏观调控得到的结果是:2004-2005年全国范围房价持续大涨,在这场调控与反击的较量过程,大连、青岛、上海、深圳等热点城市交替领跑,中国宏观经济危机被进一步加剧。
2006年,中央悍然加大调控力度,从金融、信贷、土地、税收等方面频出重手,其中包括“加息”、“征税”、“国六条”、“规定开发周期,打击土地囤积”、“90平方米70%”、“限外”、“问责”等,一系列政策步步为营,从“宏观”延伸到了“微观”,从“市场”延伸到了“政府管理”。态度不可谓不坚决,措施不可谓不严厉。
但是,全国范围房价继续坚挺,2006年10月的统计资显示,虽然全国70城市房价涨幅放缓,但总体依然处于涨势,当月房价同比上涨5.5%,环比上涨0.4%,此前一个月,这些城市房价同比上涨5.3%,环比涨幅则与10月持平,其中涨幅较大的北京,三季度房价比上一个季度涨了上千元。对这一形势,发改委官员给出的说法是,“未来的调控任务依然相当艰巨”。
为什么1993年一招“收紧银根”就可以遏制当时疯狂膨胀的房地产,而本轮调控使用了多得让人眼花缭乱的手段,却依然收效不大?
有专家认为,造成这种反差的主要原因在于,两者有了截然不同的市场机制和金融环境。
1993年,地产市场是一个以开发商为主体的市场,地方政府与开发商是一种博弈的关系,加上当时金融体系内外的流动性资本规模不大;中国经济刚刚走出调整期,城市住宅市场需求平稳;所以对房产开发“一控就灵”。但本轮调控,房地产市场,乃至金融体系的流动性资本数额与回报预期,都发生了剧烈变化。
首先,地方政府与开发商之间已超越了一种单纯的博弈关系,实际形成了利益高度一致的同盟,这一同盟对市场拥有绝对的控制权。导致调控环境变得复杂和微妙,如果调控过程地方政府严守一个市场底线,就会大大削弱调控效果。对此,前国家统计局副局长贺铿甚至直言,不打掉(这种)官商勾结,无法平抑房价。
其次是经过10余年发展,我国的金融环境也已发生巨变,从“资金短缺”变成了“流动性过剩”,据央行相关负责人透露,目前国内有高达2.6万亿的资金处于流动状态,在资本贯有的追涨心态下,它们对房地产的支撑作用不容忽视,建行董事长、外管局前任局长郭树清就曾公开表示,(目前)全国各大城市房价连续数月保持5%—7%的快速增幅,原因之一在于金融体系过剩的流动性。
本次宏观调控的失利还在于,调控的调子不到位,甚至部分调控手段是按照开发商持续数年的宣传、将当前城市商品住宅暗示为了“稀缺性公共资源”,这在一定程度上对房产市场“恰好起到了效果相反的刺激作用”。
直到2006年三季度,发改委在谈到调控目标时,依然将其定位于“稳定房价,抑制上涨”。“这样的定位不但偏离了目前房产市场的真实状况,也给了被调控者不恰当的暗示”,紧箍咒不紧,无疑会“影响调控的执行与效果”。
此外,提高首付,限制外购、征收二手房交易所得税这几项调控措施,虽可以在一定程度上抑制炒家,提高商品房市场的真需求比例,但这几个措施也同时释放出了这样的意思——商品房是一种稀缺公共资源。结果反而导致更多流动性过剩资本进场,比如在深圳,2006年有过半的交易均非自住,这部分人“全款购买比例很高,投资目的非常明确”。类似的情况也出现在另一个房价突出的城市——上海,2004年,上海商品房销售额2064.74亿元,新增房贷约800亿元,房贷额不足40%,已经显示出购房者高比例付款趋势,2005年付款比例更大比例攀升,这一年,上海商品房销售额1906.05亿元,但仅新增房贷约200亿元。这意味着,同样有“相当部分购买者在购买时是全额或高额付款,这是实力买家进场给出的一个强信号”。
介于这样严重的局势,未来的调控将走向何方?有专家建议——
在严格执行已颁布的措施,尤其是细化落实“回收闲置土地”以打击土地囤积,落实“经济廉租房”制度、以给市场确立趋于正常的价格标准这两个条款外,还应该针对一些市场苗头,启动其他几项措施,三板斧砍掉房产暴利。
第一板斧,在北京、广州、深圳、上海等热点城市,启动税收杠杆,打掉这些城市已出现的由“三级市场将高房价坐实”的现实,并遏制这一趋势向其他城市蔓延。
如果不打掉三级市场这一现状,这些城市的房价永远掉不下来,而这几个在中国具有标向意义的城市房价掉不下来,其他城市也就有了高标准的攀比对象,要降房价困难重重。房地产现在的局面就会出现僵持,可能真只有经济全盘崩溃那一天才能抑制房价。
由于这一措施是对市场进行细部操作,所以启动时需制定完善细则,同时须将不同的情况区分开来,比如对同一家庭拥有房屋套数的不同,征收额度不同的税收:第一套出租房按所得的20%征收,第二套按所得的30%征收,第三套按所得40%征收,第四套按所得50%征收……超过500平米按60%标准征收,超过1000平米按80%征收。
这一税收征收的依据是“房地产已严重干扰中国正常市场秩序,国家不鼓励私人过多占有这一公共资源”。如果出租房拥有者拒绝出租,那么应对其收取同样数额的资源闲置费,如果房主称租不出去,可将该房纳入政府专门机构,由政府按“本市实际购买力所确立的租房标准”统一对外出租,对于隐瞒拥有私房数量的情况,则可以通过“有奖举报,从重处罚”这一杀手锏予以有力还击,无限度增加这些房主的博弈风险,同时向社会开放这一领域的监督权力,以强力保证这一措施顺利推行。
第二板斧砍向“假按揭”,一些城市在房价高涨后,出现了 “将房子卖给自己也能赚钱”的畸形市场格局,导致假按揭再次抬头。而假按揭不但可以大大增加房产开发公司与市场抗衡的实力,形成巨大金融黑洞,实际也构成了金融诈骗,所以必须从严、从重进行处罚。
第三板斧砍向“目前房地产市场由权钱结合所形成的市场联盟”。
在某种程度上,这个联盟是近5年房地产持续上涨的罪魁,而且由它所衍生的市场运作机制单一,不利于形成多极化的市场供应。所以,要恢复房产市场的健康,应同时重视其他市场主体的权利。给予其他市场主体以合法地位,比如近期在国内各城市的合作建房联盟,政府就应给予明确支持引导,打破由2001年《深圳100号令》后,各地政府所实际形成的垄断型市场模式。
事实证明,个人合作建房能有效撕开此前由地方政府和开发商组成的垄断联盟,形成新的、趋于合理的市场供给,比如2006年下半年,温州个人建房合作联盟在土地交易市场争得一块被当地开发商认为价格过高、赚头不大的“鸡肋”地块后,获得了5300多元/平米的成品商品房造价,而其周边的房价为8000多元/平米。深圳,在商品房均价超过8000元/平米的情况下,由当地个人建房联盟参与的投标,竞得了一幢均价只有1600多元/米的二手房,2007年1月5日,记者在与竞投负责人林先生通电话时,他告诉记者,“他们所有参与竞投的人都哭了”,同时他还透露,春节后,他们合作建房组织将以某种方式正式参与政府组织的土地拍卖。
政府应该大力支持个人合作建房的理由还在于,发生在温州和深圳的事实表明,这一形式打掉的只是开发商的暴利,由政府主控的一级市场所损并不大。此外,由于目前个人合作建房与市场比较存在明显的成本差异,一些不法商人也可能会打着合作建房的幌子套钱,政府的积极参与,有利于这一目前尚处于雏形的市场形态的良性发育。
该学者还特别强调了调控措施中建设“廉价出租房”的重要性。他建议,未来中国城市的住宅供应,应吸取新加坡模式,以“经济适用房和廉租屋”为手段,加大政府在这一市场领域的作为,使城市房产供应重返1998年由《国务院关于进一步深化城镇住房制度改革、加快住房建设的通知》所确立的“以经济适用房为主体、以商品房为补充”的市场格局。
他认为,这不但可以为房产市场提供一个具有良性导向的新标准,而且可以转移因房地产市场变化而损失的财政收益,以上海为例,如果每年由政府按当地实际购买力提供1000万平米的廉价房,地方政府在房产领域的财政收益其实根本不会减少。
“更重要的是,这一措施也可以使中国的城市化进程得以实际启动,困扰中国多年的产业转型和社会转型问题,将随之被打开一个顺畅的通道”。
10.管理UNDO表空间.
UNDO表空间用于存放UNDO数据,当执行DML操作(INSERT,UPDATE和DELETE)时,oracle会将这些操作的旧数据写入到UNDO段,在oracle9i之前,管理UNDO数据时使用(Rollback Segment)完成的.从oracle9i开始,管理UNDO数据不仅可以使用回滚段,还可以使用UNDO表空间.因为规划和管理回滚段比较复杂,所有oracle database 10g已经完全丢弃用回滚段.并且使用UNDO表空间来管理UNDO数据.
UNDO数据也称为回滚(ROLLBACK)数据,它用于确保数据的一致性.当执行DML操作时,事务操作前的数据被称为UNDO记录.UNDO段用于保存事务所修改数据的旧值,其中存储着被修改数据块的位置以及修改前数据,
UNDO数据的作用.
1,回退事务
当执行DML操作修改数据时,UNDO数据被存放到UNDO段,而新数据则被存放到数据段中,如果事务操作存在问题,旧需要回退事务,以取消事务变化.假定用户A执行了语句UPDATE emp SET sal=1000 WHERE empno=7788后发现,应该修改雇员7963的工资,而不是雇员7788的工资,那么通过执行ROLLBACK语句可以取消事务变化.当执行ROLLBACK命令时,oracle会将UNDO段的UNDO数据800写回的数据段中.
2,读一致性
用户检索数据库数据时,oracle总是使用用户只能看到被提交过的数据(读取提交)或特定时间点的数据(SELECT语句时间点).这样可以确保数据的一致性.例如,当用户A执行语句UPDATE emp SET sal=1000 WHERE empno=7788时,UNDO记录会被存放到回滚段中,而新数据则会存放到EMP段中;假定此时该数据尚未提交,并且用户B执行SELECT sal FROM emp WHERE empno=7788,此时用户B将取得UNDO数据800,而该数据正是在UNDO记录中取得的.
3,事务恢复
事务恢复是例程恢复的一部分,它是由oracle server自动完成的.如果在数据库运行过程中出现例程失败(如断电,内存故障,后台进程故障等),那么当重启oracle server时,后台进程SMON会自动执行例程恢复,执行例程恢复时,oracl会重新做所有未应用的记录.回退未提交事务.
4,倒叙查询(FlashBack Query)
倒叙查询用于取得特定时间点的数据库数据,它是9i新增加的特性,假定当前时间为上午11:00,某用户在上午10:00执行UPDATE emp SET sal=3500 WHERE empno=7788语句,修改并提交了事务(雇员原工资为3000),为了取得10:00之前的雇员工资,用户可以使用倒叙查询特征.
使用UNDO参数
1,UNDO_MANAGEMENT
该初始化参数用于指定UNDO数据的管理方式.如果要使用自动管理模式,必须设置该参数为AUTO,如果使用手工管理模式,必须设置该参数为MANUAL,使用自动管理模式时,oracle会使用undo表空间管理undo管理,使用手工管理模式时,oracle会使用回滚段管理undo数据,
需要注意,使用自动管理模式时,如果没有配置初始化参数UNDO_TABLESPACE,oracle会自动选择第一个可用的UNDO表空间存放UNDO数据,如果没有可用的UNDO表空间,oracle会使用SYSTEM回滚段存放UNDO记录,并在ALTER文件中记载警告.
2,UNDO_TABLESPACE
该初始化参数用于指定例程所要使用的UNDO表空间,使用自动UNDO管理模式时,通过配置该参数可以指定例程所要使用的UNDO表空间.
在RAC(Real Application Cluster)结构中,因为一个UNDO表空间不能由多个例程同时使用,所有必须为每个例程配置一个独立的UNDO表空间.
3,UNDO_RETENTION
该初始化参数用于控制UNDO数据的最大保留时间,其默认值为900秒,从9i开始,通过配置该初始化参数,可以指定undo数据的保留时间,从而确定倒叙查询特征(Flashback Query)可以查看到的最早时间点.
建立UNDO表空间,
UNDO表空间专门用于存放UNDO数据,并且在UNDO表空间尚不能建立任何数据对象(表,索引,簇)
1,使用CREATE DATABASE命令建立UNDO表空间.
当使用CREATE DATABASE命令建立数据库时,通过指定UNDO TABLESPACE选项,可以建立UNDO表空间.示例如下:
CREATE DATABASE db01
…
UNDO TABLESPACE undotbs_01
DATAFILE ‘/u01/oracle/rbdb1/undo0101.dbf’ SIZE 30M;
注意:UNDO TABLESPACE 子句不是必须的,如果使用自动UNDO管理模式,并且没有指定该子句,那么建立数据库时会自动生成名为SYS_UNDOTBS的UNDO表空间.
2,使用CREATE UNDO TABLESPACE命令建立UNDO表空间.
CREATE UNDO TABLESPACE undotbs3
DATAFILE ‘D:demoundotbs3.dbf’ SIZE 10M;
修改UNDO表空间,
使用ALTER TABLESPACE命令修改UNDO表空间.
当事务用尽了UNDO表空间后,使用ALTER TABLESPACE … ADD DATAFILE增加数据文件
当UNDO表空间所在的磁盘填满是,使用ALTER TABLESPACE … RENAME DATAFIEL 命令移动数据文件到其他磁盘上.
使用ALTER DATABASE … OFFLINE/ONLINE使表空间脱机/联机.
当数据库处于ARCHIVELOG模式时,使用ALTER TABLESPACE …BEGIN BACKUP/END BACKUP命令备份UNDO表空间.
切换UNDO表空间.
启动例程并打开数据库后,同一时刻特定例程只能使用一个UNDO表空间,切换UNDO表空间是指停止例程当前使用的UNDO表空间,并启动其他UNDO表空间,下面以启用undotbs2表空间为例,说明切换UNDO表空间的方法.
ALTER SYSTEM SET undo_tablespace=undotbs02;
在RAC(Real Application Cluster)机构中,不同例程必须使用独立的UNDO表空间,而不能共用同一个UNDO表空间.
删除UNDO表空间.
当前例程正在使用的UNDO表空间是不能被删除的,如果确定要删除当前例程正在使用的UNDO表空间,应首先切换UNDO表空间.然后删除相应的UNDO表空间.
DROP TABLESPACE undotbs3;
1,确定当前例程正在使用的UNDO表空间.
Show parameter undo_tablespace
2,显示数据库的所有UNDO表空间.
SELECT tablespace_name FROMdba_tablespaces WHERE contents=’UNDO’;
3,显示UNDO表空间统计信息.
使用自动UNDO管理模式时,需要合理地设置UNDO表空间的尺寸,为例合理规划UNDO表空间尺寸,应在数据库运行的高峰阶段搜集UNDO表空间的统计信息.最终根据该统计信息确定UNDO表空间的尺寸.通过查询动态性能视图V%UNDOSTAT,可以搜集UNDO统计信息.
SELECT TO_CHAR(BEGIN_TIME,’HH24:MI:SS’) BEGIN_TIME,
TO_CHAR(END_TIME,’HH24:MI:SS’) END_TIME,
UNDOBLKS
FROM V$UNDOSTAT;
BEGIN_TIME用于标识起始统计时间,END_TIME用于标识结束统计时间,UNDOBLKS用于标识UNDO数据所占用的数据块个数.oracle每隔10分钟生成一行统计信息.
4,显示UNDO段统计信息.
使用自动UNDO管理模式时,oracle会在UNDO表空间上自动建立10个UNDO段,通过查询动态信息视图V$ROLLNAME,可以显示所有联机UNDO段的名称,通过查询动态性能视图V$ROLLLISTAT,可以显示UNDO段的统计信息.通过在V$ROLLNAME和V$ROLLLISTAT之间执行连接查询,可以监视特定UNDO段的特定信息.
SELECT a.name, b.xacts, b.writes, b.extents
FROM v$rollname a, v$rollstat b
WHERE a.usn=b.usn;
Name用于标识UNDO段的名称,xacts用于标识UNDO段所包含的活动事务个数,
Writes用于标识在undo段上所写入的字节数,extents用于标识UNDO段的区个数.
5,显示活动事务信息.
当执行DML操作时,oracle会将这些操作的旧数据放到UNDO段中,动态性能视图v$session用于显示会话的详细信息,动态性能视图v$transaction用于显示事务的详细信息,动态性能视图v$rollname用于显示联机UNDO段的名称.通过在这3个动态性能视图之间执行连接查询,可以确定正在执行事务操作的会话,事务所使用的UNDO段,以及事务所占用的UNDO块个数.
Col username format a10
Col name format a10
SELECT a.username, b.name, c.used_ublk
FROM v$session a, v$rollname b, v$transaction c
WHERE a.saddr=c.ses_addr AND b.usn=c.xidusn
AND a.username=’SCOTT’;
6,显示UNDO区信息
数据字典视图dba_undo_extents用于显示UNDO表空间所有区的详细信息.包括UNDO区尺寸和状态等信息.
SELECT extend_id, bytes, status FROM dba_undo_extents
WHERE segment_name’_SYSSMU5$’;
其中,extent_id用于标识区编号,bytes用于标识区尺寸,status用于标识区状态(ACTIVE:表示该区处于活动状态,EXPIRED:标识该区未用).
sqlplus /nolog
SQL*Plus: Release 9.2.0.4.0 - Production on Sat Aug 6 14:24:49 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
SQL> connect / as sysdba;
Connected.
-- 把HTTP/WEBDAV端口从8080改到8081
SQL> call dbms_xdb.cfg_update(updateXML(dbms_xdb.cfg_get(),
2 '/xdbconfig/sysconfig/protocolconfig/httpconfig/http-port/text()',8081))
3 /
Call completed.
-- 把FTP端口从2100改到2111
SQL> call dbms_xdb.cfg_update(updateXML(dbms_xdb.cfg_get(),
2 '/xdbconfig/sysconfig/protocolconfig/ftpconfig/ftp-port/text()',2111))
3 /
Call completed.
SQL> commit;
Commit complete.
SQL> exec dbms_xdb.cfg_refresh;
PL/SQL procedure successfully completed.
-- 检查修改是否已经成功
SQL> select dbms_xdb.cfg_get from dual;
CFG_GET
--------------------------------------------------------------------------------
<xdbconfig xmlns="http://xmlns.oracle.com/xdb/xdbconfig.xsd" xmlns:xsi="http://w
用Linux构建最好的FTP服务器
|
|
2005-01-10 15:03:32 来自:http://www.zhcedu.com.cn |
|
在众多网络应用中,FTP(文件传输协议)有着非常重要的地位。Internet中一个十分重 要的资源就是软件资源,而各种各样的软件资源大多数都放在FTP服务器中。与大多数 Internet服务一样,FTP也是一个客户机/服务器系统。用户通过一个支持FTP协议的客户 机程序,连接到主机上的FTP服务器程序。用户通过客户机程序向服务器程序发出命令, 服务器程序执行用户发出的命令,并将执行结果返回给客户机。FTP服务可以根据服务对 象的不同分为两类:系统FTP服务器只允许系统上的合法用户使用;匿名FTP服务器 (Anonymous FTP Server)允许任何人登录到FTP服务器去获取文件。
FTP的数据传输模式针对FTP数据连接而言,分为主动传输模式、被动传输模式和单端口传 输模式三种。
1.主动传输模式 当FTP的控制连接建立,客户提出目录列表、传输文件时,客户端发出PORT命令与服务器 进行协商,FTP服务器使用一个标准端口20作为服务器端的数据连接端口(ftp-data), 与客户建立数据连接。端口20只用于连接源地址是服务器端的情况,并且端口20没有监听 进程来监听客户请求。
在主动传输模式下,FTP的数据连接和控制连接方向相反,由服务器向客户端发起一个用 于数据传输的连接。客户端的连接端口由服务器端和客户端通过协商确定。
2.被动传输模式 当FTP的控制连接建立,客户提出目录列表、传输文件时,客户端发送PASV命令使服务器 处于被动传输模式,FTP服务器等待客户与其联系。FTP服务器在非20端口的其它数据传输 端口上监听客户请求。
在被动传输模式下,FTP的数据连接和控制连接方向一致,由客户端向服务器发起一个用 于数据传输的连接。客户端的连接端口是发起该数据连接请求时使用的端口。当FTP客户 在防火墙之外访问FTP服务器时,需要使用被动传输模式。
3.单端口模式 除上述两种模式之外,还有一种单端口模式。该模式的数据连接请求由FTP服务器发起。 使用该传输模式时,客户端的控制连接端口和数据连接端口一致。因为这种模式无法在短 时间连续输入数据、传输命令,因此并不常用。
Linux下有很多可用的FTP服务器,其中比较流行的有WU-FTP(Washington University FTP)和VSFTP。Red Hat 8.0中自带了WU-FTP和VSFTP两个软件。WU-FTP是一个著名的FTP 服务器软件,它功能强大,能够很好地运行于众多Unix操作系统中。不过作为后起之秀的 VSFTP越来越流行,在Red Hat 9.0发行版中就只带有VSFTP。
VSFTP中VS的意思是“Very Secure”。从名称可以看出,从一开始,软件的编写者就非常注 重其安全性。除与生俱来的安全性外,VSFTP还具有高速、稳定的性能特点。在稳定性方 面,VSFTP可以在单机(非集群)上支持4000个以上的并发用户同时连接。据 ftp.redhat.com的数据,VSFTP最多可以支持15000个并发用户。
快速构建FTP服务器
FTP服务器实现的基本功能是上传下载,下面就分几个步骤来搭建一个可以实现下载功能 的简易FTP服务器。
1.安装FTP服务器 如果在安装系统时没有选择安装FTP服务器,可以通过Red Hat 9.0中的“添加/删除应用程 序”工具进行安装。具体方法是,选择“主选单”→“系统设置”→“添加/删除应用程序”,在弹 出的界面中选中FTP服务器,单击“更新”即可。
如果无法确认是否安装了该软件,可以使用以下命令查看:
#rpm -qa|grep vsftpd vsftpd-1.1.3-8
2.启动FTP服务器 套用Red Hat 9.0的预设范例直接启动VSFTP。 # /sbin/service vsftpd start
为vsftpd启动vsftpd: [确定]
3.在/var/ftp/pub目录下创建一个名为test.txt的文件,文件内容为“This is a test file”。
4.测试 使用FTP客户端登录到本地服务器,然后以匿名身份(anonymous)登录:
# ftp 127.0.0.1 Connected to 127.0.0.1 (127.0.0.1). 220 (vsFTPd 1.1.3) Name (127.0.0.1:root): anonymous 331 Please specify the password. Password: 230 Login successful. Have fun. Remote system type is UNIX. Using binary mode to transfer files.
这样就成功地登录到FTP服务器。可以显示服务器目录列表如下:
ftp> ls 227 Entering Passive Mode (127,0,0,1,63,15) drwxr-xr-x 2 0 0 4096 Dec 04 01:35 pub 226 Directory send OK.
切换到pub目录下,并显示目录内容,可以找到刚才创建的文件test.txt:
ftp> cd pub 250 Directory successfully changed. ftp> ls 227 Entering Passive Mode (127,0,0,1,232,34) 150 Here comes the directory listing. -rw-r--r-- 1 0 0 21 Dec 04 01:35 test.txt 226 Directory send OK.
下载test.txt文件:
ftp> mget test.txt mget test.txt? y 227 Entering Passive Mode (127,0,0,1,186,210) 150 Opening BINARY mode data connection for test.txt (21 bytes). 226 File send OK. 21 bytes received in 0.0108 secs (1.9 Kbytes/sec)
查看本机目录内容,可以看到test.txt已成功下载到本机。
ftp> !ls a EIO_Binders initrd mnt proc tftpboot ylg.txt bin etc lib mymnt root tmp boot home lost+found myshare sbin usr dev id_dsas.pub misc opt test.txt var
尝试上传名为ylg.txt的文件,可以看到请求被拒绝了。
ftp> put ylg.txt local: ylg.txt remote: ylg.txt 227 Entering Passive Mode (127,0,0,1,243,10) 550 Permission denied.
退出登录:
ftp> bye 221 Goodbye.
由测试可以看出,已经可以下载文件,但不能上传文件(也不能在服务器上创建目录和文 件)。实际上这是一个专门提供下载服务的匿名FTP服务器。
从上面的步骤可以看出,并不需要做什么配置就可以完成一个简易FTP服务器的架设。这 是因为Red Hat已经配置好一个缺省的FTP服务器。不过在实际应用中,大部分情况下这个 简易的服务器并不能满足需求。
进一步配置FTP服务器
下面将创建一个能够满足常用需求的FTP服务器。实际应用中,FTP服务器一般要同时提供 上传和下载功能。此外,出于安全考虑,还需要有用户身份验证、用户权限设置及空间管 理等。下面就来搭建这样一个FTP服务器。
1.创建欢迎语。如果希望使用者在进入目录时,能够看到欢迎语或对本目录的介绍,可 以通过以下方法来实现。
确定/etc/vsftpd/vsftpd.conf文件中dirmessage_enable=YES,默认情况下,Red Hat 9.0有此设置。接着,在目录中新增名为.message的文件。本例在/home/ylg目录下创建一 个.message文件,其内容为“欢迎来到我的FTP站点”。
2.更换FTP服务器的默认端口。将预设的21端口改为2121,这样做是基于安全的考虑。更 改方法为,使用vi打开/etc/vsftpd/vsftpd.conf: #vi /etc/vsftpd/vsftpd.conf
在文件最后增加如下一行内容: listen_port=2121
3.取消anonymous登录的功能。在vsftpd.conf文件中找到如下一行,并将其值改为“NO”: anonymous_enable=YES
4.设定使用者不得更改目录。这样做的目的也是基于安全性的考虑。一般情况下,使用 者的预设目录为/home/username。若是不希望使用者在登录后能够切换至上一层目录 /home,则可通过以下设置来实现。在/etc/vsftpd/vsftpd.conf文件中找到以下三行内容:
#chroot_list_enable=YES # (default follows) #chroot_list_file=/etc/vsftpd.chroot_list
将其改为:
chroot_list_enable=YES # (default follows) chroot_list_file=/etc/vsftpd/chroot_list
新增一个文件/etc/vsftpd/chroot_list,文件内容为两个用户名:
ylg user1
5.针对不同的使用者限制不同的速度。假设用户ylg所能使用的最高速度为500Kb/s,用 户user1所能使用的最高速度为250Kb/s,可以通过以下方法设置。在 /etc/vsftpd/vsftpd.conf文件尾部新增以下一行: user_config_dir=/etc/vsftpd/userconf 增加一个名为/etc/vsftpd/userconf的目录: #mkdir /etc/vsftpd/userconf
在/etc/vsftpd/userconf下新增一个名为ylg的文件,其内容如下所示: local_max_rate=500000
在/etc/vsftpd/userconf目录下新增一个名为user1的文件,其内容如下所示: local_max_rate=250000
VSFTP对于速度的限制范围大概在80%到120%之间,也就是限制最高速度为100Kb/s,但 实际的速度可能在80Kb/s到120Kb/s之间。如果频宽不足,数值会低于此限制。
6.对于每一个联机用户,都以独立的进程来运行。一般情况下,在启动VSFTP时,只会看 到一个名为vsftpd的进程在运行。但若是读者希望每一个联机用户都能以独立的进程来呈 现,则可通过在/etc/vsftpd/vsftpd.conf文件中增加以下一行来实现: setproctitle_enable=YES
7.保存/etc/vsftpd/vsftpd.conf文件,然后重新启动vsftpd: #service vsftpd restart
8.测试刚创建的FTP服务器。 以缺省方式登录会被拒绝,因为此时的默认端口号已经更改为2121,所以登录时需指定端 口。
# ftp 127.0.0.1 ftp: connect: Connection refused
此时也不能再使用匿名方式登录:
# ftp 127.0.0.1 2121 Connected to 127.0.0.1 (127.0.0.1). 220 (vsFTPd 1.1.3) Name (127.0.0.1:root): anonymous 331 Please specify the password. Password: 530 Login incorrect. Login failed.
如果以用户ylg则可以成功登录(指定端口2121),并显示欢迎信息:
# ftp 127.0.0.1 2121 Connected to 127.0.0.1 (127.0.0.1). 220 (vsFTPd 1.1.3) Name (127.0.0.1:root): ylg 331 Please specify the password. Password: 230-欢迎来到我的FTP站点 230 Login successful. Have fun. Remote system type is UNIX. Using binary mode to transfer files.
因为在设置中设定了不能切换目录,所以下列命令无法正确执行:
ftp> cd /home 550 Failed to change directory.
再来测试一下上传和下载。首先下载服务器目录中的test.txt文件:
ftp> get test.txt local: test.txt remote: test.txt 227 Entering Passive Mode (127,0,0,1,243,215) 150 Opening BINARY mode data connection for test.txt (21 bytes). 226 File send OK. 21 bytes received in 0.00308 secs (6.7 Kbytes/sec)
可以通过!ls命令看到本机目录中已成功下载该文件。然后上传本机目录中的ylg.txt文件 到服务器:
ftp> put ylg.txt local: ylg.txt remote: ylg.txt 227 Entering Passive Mode (127,0,0,1,133,248) 150 Ok to send data. 226 File receive OK. 19 bytes sent in 0.0401 secs (0.46 Kbytes/sec)
用ls命令查看服务器目录,会发现该文件已成功上传。
为了测试不同连机用户使用的是不同进程,可以使用ps -ef指令,显示如下所示:
# ps -ef|grep ftp root 12972 1356 0 13:44 pts/1 00:00:00 ftp 127.0.0.1 2121 nobody 12973 12908 0 13:44 ? 00:00:00 [vsftpd] ylg 12975 12973 0 13:44 ? 00:00:00 [vsftpd] user1 13013 13011 0 13:46 ? 00:00:00 [vsftpd] root 13041 13015 0 13:47 pts/4 00:00:00 grep ftp
到现在为止,一个基本可以满足普通使用需求的FTP服务器就已经架设完成。
在实际应用中,有时为了增加安全性,会将FTP服务器置于防火墙之后。如本文开头所 述,被动传输模式适合于带有防火墙的情况。下面就来创建一个防火墙后的FTP服务器, 该服务器FTP端口为2121,数据传输端口为2020。
执行以下两行指令,只允许2121和2020端口打开,其余端口关闭:
#iptables -A INPUT -p tcp -m multiport --dport 2121,2020 -j ACCEPT #iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset
修改/etc/vsftpd/vsftpd.conf文件,在文本最后添加以下两行:
listen_port=2121 ftp_data_port=2020
重新启动vsftpd: #service vsftpd restart
有时希望直接在/etc/hosts.allow中定义允许或拒绝某一源地址,可以通过以下配置来实 现。先确保/etc/vsftpd/vsftpd.conf中tcp_wrappers=YES,Red Hat 9.0中,这是默认 值。重新启动vsftpd: #service vsftpd restart
假设提供168.192.2.1和210.31.8.1到210.31.8.254的连接,则可对/etc/hosts.allow进 行如下设定:
vsftpd : 168.192.2.1 210.31.8. : allow ALL : ALL : DENY
配置虚拟用户FTP
上面配置的FTP服务器有一个特点,就是FTP服务器的用户本身也是系统用户。这显然是一 个安全隐患,因为这些用户不仅能够访问FTP,也能够访问其它的系统资源。如何解决这 个问题呢?答案就是创建一个虚拟用户的FTP服务器。虚拟用户的特点是只能访问服务器 为其提供的FTP服务,而不能访问系统的其它资源。所以,如果想让用户对FTP服务器站内 具有写权限,但又不允许访问系统其它资源,可以使用虚拟用户来提高系统的安全性。
在VSFTP中,认证这些虚拟用户使用的是单独的口令库文件(pam_userdb),由可插入认 证模块(PAM)认证。使用这种方式更加安全,并且配置更加灵活。 下面介绍配置过程。
1.生成虚拟用户口令库文件。为了建立此口令库文件,先要生成一个文本文件。该文件 的格式如下,单数行为用户名,偶数行为口令:
#vi account.txt ylg 1234 zhanghong 4321 gou 5678
2.生成口令库文件,并修改其权限:
#db_load -T -t hash -f ./account.txt /etc/vsftpd/account.db #chmod 600 /etc/vsftpd/account.db
3.新建一个虚拟用户的PAM文件。加上如下两行内容:
#vi /etc/pam.d/vsftp.vu auth required /lib/security/pam_userdb.so db=/etc/vsftpd/account account required /lib/security/pam_userdb.so db=/etc/vsftpd/account
4.建立虚拟用户,设置该用户所要访问的目录,并设置虚拟用户访问的权限:
#useradd -d /ftpsite virtual_user #chmod 700 /ftpsite
经过该步骤的设置,/ftpsite就是virtual_user用户的主目录,该用户也是/ftpsite目录 的拥有者。除root用户之外,只有该用户具有对该目录的读、写和执行的权限。
5.生成一个测试文件。先切换至virtual_user用户身份,然后在/ftpsite目录下创建一 个文件:
#su -virtual_user $vi /ftpsite/mytest This is a test file. $su - root
6.编辑/etc/vsftpd/vsftpd.conf文件,使其整个文件内容如下所示(去掉了注释内容):
anonymous_enable=NO local_enable=YES local_umask=022 xferlog_enable=YES connect_from_port_20=YES xferlog_std_format=YES listen=YES write_enable=YES anon_upload_enable=YES anon_mkdir_write_enable=YES anon_other_write_enable=YES one_process_model=NO chroot_local_user=YES ftpd_banner=Welcom to my FTP server. anon_world_readable_only=NO guest_enable=YES guest_username=virtual_user pam_service_name=vsftp.vu
上面代码中,guest_enable=YES表示启用虚拟用户;guest_username=virtual则是将虚拟 用户映射为本地用户,这样虚拟用户登录后才能进入本地用户virtual的目录/ftpsite; pam_service_name=vsftp.vu指定PAM的配置文件为vsftp.vu。
7.重新启动VSFTP: #service vsftpd restart
8.以虚拟用户gou(Linux中并无该账号)进行测试:
# ftp 127.0.0.1 Connected to 127.0.0.1 (127.0.0.1). 220 Welcom to my FTP server. Name (127.0.0.1:root): gou 331 Please specify the password. Password: 230 Login successful. Have fun. Remote system type is UNIX. Using binary mode to transfer files.
测试下载服务器目录中的一个文件mytest:
ftp> get mytest local: mytest remote: mytest 227 Entering Passive Mode (127,0,0,1,159,19) 150 Opening BINARY mode data connection for mytest (21 bytes). 226 File send OK. 21 bytes received in 0.00038 secs (54 Kbytes/sec)
测试上传本机目录中的文件vsftpd.conf:
ftp> !ls account.db chroot_list k mytest userconf vsftpd.conf ftp> put vsftpd.conf local: vsftpd.conf remote: vsftpd.conf 227 Entering Passive Mode (127,0,0,1,117,203) 150 Ok to send data. 226 File receive OK. 4229 bytes sent in 0.00195 secs (2.1e+03 Kbytes/sec)
可以看到,使用没有系统账号的虚拟用户可以成功完成上传、下载的工作。但该FTP虚拟 服务器只允许虚拟用户登录,其它系统用户无法登录,如系统用户user1不是虚拟用户, 则不能登录该虚拟服务器。
# ftp 127.0.0.1 Connected to 127.0.0.1 (127.0.0.1). 220 Welcom to my FTP server. Name (127.0.0.1:root): user1 331 Please specify the password. Password: 530 Login incorrect. Login failed.
在虚拟FTP服务器中,也可以对各个用户的权限进行设置。方法是在/etc/vsftpd.conf文 件中添加如下一行: user_config_dir=用户配置文件目录
然后在用户配置文件目录下创建相应的用户配置文件,比如为上述名为gou的用户创建一 个配置文件(假设配置文件目录为/etc/user_config_dir):
#vi /etc/user_config_dir/gou write_enable=NO anono_upload_enable=NO
重启FTP服务器,这时再使用账号gou来登录,就已经没有上传的权限了。
|
如何更改Oracle的默认监听端口(1521)
Oracle
的默认监听端口为1521,你可以方便的依照下列步骤修改为其他端口:
[oracle@dbserver2 admin]$ cd $ORACLE_HOME/network/admin
[oracle@dbserver2 admin]$ vi listener.ora
修改这个文档中的1521端口为你需要的端口your port
[oracle@dbserver2 admin]$ lsnrctl stop
[oracle@dbserver2 admin]$ lsnrctl start
修改客户端的连接
Oracle 9i开始,创建数据库时默认包含了XDB特性。一旦启动了数据库和Listener,Oracle XDB的http服务就占用8080端口,刚好和JBoss、Tomcat等默认端口冲突。除了修改Tomcat等外,还可以修改XDB的配置。
Oracle XDB的端口设置不在配置文件中,而是在数据库里。修改XDB的http和ftp服务端口的方法有3种:
1.使用sys登录Oracle,利用dbms_xdb修改端口设置
SQL> -- Change the HTTP/WEBDAV port from 8080 to 8081
SQL> call dbms_xdb.cfg_update(updateXML(
2 dbms_xdb.cfg_get()
3 , '/xdbconfig/sysconfig/protocolconfig/httpconfig/http-port/text()'
4 , 8081))
5 /
Call completed.
SQL> -- Change the FTP port from 2100 to 2111
SQL> call dbms_xdb.cfg_update(updateXML(
2 dbms_xdb.cfg_get()
3 , '/xdbconfig/sysconfig/protocolconfig/ftpconfig/ftp-port/text()'
4 , 2111))
5 /
Call completed.
SQL> COMMIT;
Commit complete.
SQL> EXEC dbms_xdb.cfg_refresh;
PL/SQL procedure successfully completed.
2.使用OEM console,选择数据库,XML Database,Configuration。更改XDB的有关设置。
3.去掉数据库的初始化参数: dispatchers='(PROTOCOL=TCP) (SERVICE=XDB)',将会禁止XDB的http和ftp服务。
proc cpp_suffix=cpp code=cpp txsPublic.pcc define=__linux UNSAFE_NULL=YES MODE=ORACLE DBMS=V8 CHAR_MAP=STRING INCLUDE=/home/tax/100/src/public/include INCLUDE=/home/tax/100/src/server/include INCLUDE=/usr/oracle/product/9.2.0/precomp/public
Pro*C/C++: Release 9.2.0.6.0 - Production on Fri Nov 24 17:07:54 2006
Syntax error at line 52, column 37, file /usr/include/c++/3.4.3/bits/concept_check.h:
Error at line 52, column 37 in file /usr/include/c++/3.4.3/bits/concept_check.h
#define __glibcxx_function_requires(...)
....................................1
PCC-S-02014, Encountered the symbol "..." when expecting one of the following:
) an identifier, define, elif, else, endif, error, if, ifdef,
ifndef,
解决方法:
1、配置好 $ORACLE_HOME/precomp/admin/pcscfg.cfg 文件
根据你的系统不同,大概像这样
sys_include=(/usr/lib/gcclib/i386redhatlinux/3.2.2/include,/u01/oracle/product/8.1.7/precomp/public,/usr/include,/usr/include/sys)
include=(/u01/oracle/product/8.1.7/precomp/public)
include=/u01/oracle/product/8.1.7/precomp/hdrs
include=/u01/oracle/product/8.1.7/tpcc2x_2/src
include=/u01/oracle/product/8.1.7/precomp/precomp/include
include=/u01/oracle/product/8.1.7/precomp/oracore/include
include=/u01/oracle/product/8.1.7/precomp/oracore/public
include=/u01/oracle/product/8.1.7/precomp/rdbms/include
include=/u01/oracle/product/8.1.7/precomp/rdbms/public
include=/u01/oracle/product/8.1.7/precomp/rdbms/demo
include=/u01/oracle/product/8.1.7/precomp/nlsrtl/include
include=/u01/oracle/product/8.1.7/precomp/nlsrtl/public
include=/u01/oracle/product/8.1.7/precomp/network_src/include
include=/u01/oracle/product/8.1.7/precomp/network_src/public
include=/u01/oracle/product/8.1.7/precomp/network/include
include=/u01/oracle/product/8.1.7/precomp/network/public
include=/u01/oracle/product/8.1.7/precomp/plsql/public
ltype=short
2、预编译的时候加上 parse=none 选项
proc iname=*** parse=none
3、编译的时候要链接oracle的库文件, 加选项 -lclntsh
下面是我参照别的网友的一个makefile文件,
INCLDIR= -I. -I/u01/oracle/product/8.1.7/precomp/public -I/u01/oracle/product/8.1.7/rdbms/public -I/u01/oracle/product/8.1.7/rdbms/demo -I/u01/oracle/product/8.1.7/plsql/public -I/u01/oracle/product/8.1.7/network/public
DFLAGS= -DPRECOMP -DLINUX -D_GNU_SOURCE -DSLTS_ENABLE -DSLMXMX_ENABLE -D_REENTRANT -DNS_THREADS
CFLAGS= -O3
LIBPATH= -L/u01/oracle/product/8.1.7/lib/
LIBS=-lclntsh `cat /u01/oracle/product/8.1.7/lib/sysliblist` -ldl -lm
myproc:myproc.pc
proc parse=no iname=myproc.pc include=$(ORACLE_HOME)/precomp/public
gcc -o $@ $@.c -I$(ORACLE_HOME)/precomp/public -L$(ORACLE_HOME)/lib -lclntsh -lclient8 $(LIBPATH) $(LIBS)
clean:
rm *.lis myproc.c myproc
单点登录的简单实现
在门户项目中,经常会遇到如何实现单点登录的问题,下面就本人的经验做个总结。欢迎大家进行补充讨论。
单点登录的具体实现有很多种选择,包括:
- 采用专门的SSO商业软件: 主要有:Netgrity的Siteminder,已经被CA收购。Novell 公司的iChain。RSA公司的ClearTrust等。
- 采用门户产品供应商自己的SSO产品,如:BEA的WLES,IBM 的Tivoli Access Manager,Sun 公司的identity Server,Oracle公司的OID等。
- 这些商业软件一般适用于客户对SSO的需求很高,并且企业内部采用COTS软件如:Domino,SAP,Sieble的系统比较多的情况下采用。并结合身份管理。统一认证等项目采用。采用这些软件一般都要对要集成的系统做些改造,如在要集成的系统上安装AGENT。现在一般只提供常见软件如:Domino,SAP,Sieble,常见应用服务器:weblogic,websphere等的AGENT。要先统一这些系统的认证。一般采用LDAP或数据库。然后才能实现SSO。比较麻烦。
- 另外,如果不想掏银子,也有OPEN SOURCE的SSO软件可选:主要有:http://www.josso.org/https://opensso.dev.java.net/http://www.sourceid.org 等。具体怎么样就不清楚了。
如果项目对SSO的要求比较低,又不想对要被集成的系统做任何改动,可采用下面介绍的方式简单实现:下面我们通过一个例子来说明。假如一个门户项目要对下面的几个系统做SSO。
用户在这些系统中的用户名,密码各不相同,如:员工号为001的员工在这些系统中的用户名,密码分别如下:
用户 |
系统 |
用户名 |
密码 |
001 |
Portal系统 |
A |
1234 |
001 |
邮件系统 |
B |
2345 |
001 |
DOMINO系统 |
C |
AAAA |
001 |
报销系统 |
D |
CCCC |
001 |
工资系统 |
E |
BBBB |
首先,建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系
首先,要建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系并保存。可保存在表中或LDAP中或文件系统中。当然要考虑这些系统之间的数据同步问题。比较好的方式是找到用户在这些系统中的都存在的唯一信息(如员工号,MAIL地址,姓名等)。通过唯一信息实时到各个系统中去取认证所需要的信息。就不需要考虑数据同步问题。比较实用。可以建立类似下面的表:密码可采用加密保存。如果是采用BEA的Weblogic Portal,可采用UUP来保存这些信息。
(
user varchar2(20), /*用户名*/
app_name varchar2(20), /*应用系统*/
architect varchar2(4), /*应用系统的架构BS或CS*/
app_company varchar2(50), /*用户所属分公司*/
app_department varchar2(50), /*用户所在的部门*/
app_user varchar2(15), /*在该系统中的用户名*/
app_passwd varchar2(15), /*在该系统中的密码*/
app_cookie varchar2(30), /*COOKIE名称*/
form_user varchar2(20), /*认证页面中FORM的用户名字段*/
form_passwd varchar2(20), /*认证页面中FORM的密码字段*/
app_special varchar2(20) /*其他*/
);
通过IFRAME或超连接方式集成目标系统,并进行SSO
通过IFRAME或超连接方式集成目标系统,并在URL中带上用户名和密码。如集成DOMINO可采用如下方式:
<IFRAME src=http://host1/names.nsf?Login&Username=admin&Password=pass&RedirectTo=/names.nsf
width="100%" frameborder="0" align="middle" height="100%" hspace="0" marginheight="0" marginwidth="0" scrolling="yes" style="background-color:#f7f7ff;">
</IFRAME>
或:
Href src=“http:// host1/names.nsf?Login&Username=admin&Password=pass&RedirectTo=/names.nsf”
以上采用的是在HTTP中直接传递明码,为提高安全性,可采用HTTPS来传递用户名和密码。另外采用这种方式被集成的系统必须支持FORM方式认证。J2EE应用,DOMINO等都支持FORM认证。
这两种方式如果SSO成功,就自动进入目标系统的界面,如果实现会显示目标系统的登录界面。其效果图如下:
这种方式,必须维护对应关系表,如上面的sso_info。更好的方式是提供界面,让最终用户自己维护这种对应关系,可模仿Compoze portlets for lotus的做法,在用户第一次进入要与之做SSO的系统时,如DOMINO系统,显示一个界面,让用户自己输入他在该系统中的用户名/密码等信息。并保存到表中或LDAP等其他数据源中。以后用户要进入这些系统时,就直接从表中或其他数据源中取用户的用户名/密码等信息,帮助用户做认证。建议采用这种方式。如下图所示。如果用户改变了自己在DOMINO系统中的用户名,密码。从门户系统进入DOMINO系统时,认证会失败,就重新显示类似下面的界面。让用户重新输入他在DOMINO系统中新的用户名,密码并保存。
以上这种实现方式,一般需要浏览器支持COOKIE,所以要注意浏览器的配置,在开发阶段,为方便调试,可设置IE,让它显示COOKIE的名称。如下所示:
采用这种方式,对要集成的系统不需要做任何的改动。如果PORTAL系统中的用户在被集成的系统中的权限都一样,可采用建立一个通用用户的做法。也就是所有在PORTAL系统中的用户都采用这个通用用户进入目标系统。这种方式等于是采用页面集成方式做集成。比较方便使用。另外,有时候需要采用调用API,或配置Adapter等应用集成方式来集成其他系统,一般也是通过定义一个连接专用的用户。在API中或在配置Adapter的时候写死。如采用JAVA API方式集成DOMINO:
lotus.domino.Session dominoSession = NotesFactory.createSession(dominoServer, “admin”, “password”);
CS结构实现方式
经常有人问CS结构的应用如何实现SSO,本人的建议是对这种系统不要自己去实现SSO。很麻烦,其实输个用户名,密码没什么大不了的。如果要实现,一是采用商业软件。另外也可以采用以下方式:在PORTAL的PORTLET上建立超连接。并通过APPLET方式启动CS结构的应用系统的登录界面。然后通过如下的方式把用户名/密码传递过去。
-不能做任何改动的客户端 - WIN消息(给登录窗口发送用户名,密码等登录所需要的信息),模拟键盘(java有模拟键盘输入的API)
-可以做改动的客户端 - 参数传递,并让登录的EXE文件读取参数进行认证。
因为要让APPLET执行本地的EXE文件,所以必须对IE中的JRE的安全进行设置。
其他:
在采用以上方式实现了SSO后,要注意LOGOUT,可采用与LOGIN相同的方式。也可以通过被集成系统的超时设置来实现。
单点登录SSO技术资料收集
对UUID几乎没有了解,google了一下,才知道是128位整数(16字节)的全局唯一标识符(Universally Unique Identifier)。
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成UUID的API。UUID按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得),UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。
在ColdFusion中可以用CreateUUID()函数很简单的生成UUID,其格式为:xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
,可以从
cflib 下载
CreateGUID() UDF进行转换。
使用UUID的好处在分布式的软件系统中(比如:DCE/RPC, COM+,CORBA)就能体现出来,它能保证每个节点所生成的标识都不会重复,并且随着WEB服务等整合技术的发展,UUID的优势将更加明显。
关于UUID的更多信息可以多
google 一下。
|
|
|
Oracle9i 2.0.4在Red Hat Enterprise Linux AS 3上的安装
|
|
|
Oracle9i 2.0.4在Red Hat Enterprise Linux AS 3上的安装 (我测试了两遍都OK) 1、确认安装了以下软件包 [oracle@Gledeson oracle]$ rpm -qa | grep openmotif openmotif21-2.1.30-8 openmotif-devel-2.2.3-3.RHEL3 openmotif-2.2.3-3.RHEL3 [oracle@Gledeson oracle]$ rpm -qa |grep setarch setarch-1.3-1 [oracle@Gledeson oracle]$ rpm -qa|grep compat compat-gcc-c++-7.3-2.96.128 compat-glibc-7.x-2.2.4.32.6 compat-slang-1.4.5-5 compat-pwdb-0.62-3 compat-libstdc++-devel-7.3-2.96.128 compat-db-4.0.14-5 compat-gcc-7.3-2.96.128 compat-libstdc++-7.3-2.96.128
2、建立Oracle帐号 groupadd dba groupadd oinstall useradd -c "Oracle software owner" -g oinstall -G dba oracle passwd oracle 3、建立文件目录 su root mkdir /u01 mkdir /u01/oracle mkdir /u01/oracle/product mkdir /u01/oracle/product/9.2.0 chown -R oracle.oinstall /u01/oracle mkdir /var/opt/oracle chown oracle.dba /var/opt/oracle chmod 755 /var/opt/oracle 4、设置环境变量 用Oracle帐号执行下列命令:(或直接copy到.bash_profile文件中) # Set the LD_ASSUME_KERNEL environment variable only for Red Hat 9, # RHEL AS 3, and RHEL AS 4 !! # Use the "Linuxthreads with floating stacks" implementation instead of NPTL: #export LD_ASSUME_KERNEL=2.4.1 # for RH 9 and RHEL AS 3 #export LD_ASSUME_KERNEL=2.4.19 # for RHEL AS 4 export LD_ASSUME_KERNEL=2.4.1 export ORACLE_BASE=/u01/oracle export ORACLE_HOME=$ORACLE_BASE/product/9.2.0 export ORACLE_SID=ORADB01 export ORACLE_TERM=xterm export ORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib export LD_LIBRARY_PATH # Set shell search paths export PATH=$PATH:$ORACLE_HOME/bin 保存后退出. 执行: source .bash_profile 然后。退出登录,再次进入,这时候oracle的环境就已经生效了 5、设置内核参数 su root 修改 /etc/sysctl.conf 这个文件,加入以下的语句: kernel.shmmax = 2147483648 kernel.shmmni = 4096 kernel.shmall = 2097152 kernel.sem = 250 32000 100 128 fs.file-max = 65536 net.ipv4.ip_local_port_range = 1024 65000 参数说明: sem 4个参数依次为SEMMSL(每个用户拥有信号量最大数); SEMMNS(系统信号量最大数); SEMOPM(每次semopm系统调用操作数); SEMMNI(系统辛苦量集数最大数). Shmmax 最大共享内存2GB 物理内存如果小的话可以设置成 536870912. Shmmni 最小共享内存 4096KB. Shmall 所有内存大小. 6、安装补丁 (ORACLE9i 9204版,只需在运行runInstall前打p3006854_9204_LINUX.zip 补丁即可, 其他补丁不用再打) 安装补丁前,却换到root用户的控制台,打补丁前执行以下语句: su root mv /usr/bin/gcc /usr/bin/gcc323 ln -s /usr/bin/gcc296 /usr/bin/gcc mv /usr/bin/g++296 /usr/bin/g++ ln -s /usr/bin/g++ /usr/bin/g++296 [root@localhost /]# sh rhel3_pre_install.sh Applying patch... Ensuring permissions are correctly set... Done. Patch successfully applied 7、解压文件 zcat ship_9204_linux_disk1.cpio | cpio -idmv zcat ship_9204_linux_disk2.cpio | cpio -idmv zcat ship_9204_linux_disk3.cpio | cpio -idmv 这时生成三个目录:Disk1、Disk2、Disk3 8、cd到Disk1中 cd /Disk1 ./runInstall.sh
| | |
对String的深刻理解
刚开始玩java,对其String类,使用有些感触;
1、"abc"与new String("abc");
经常会问到的面试题:String s = new String("abc");创建了几个String Object?【如这里创建了多少对象? 和一道小小的面试题 】
这个问题比较简单,涉及的知识点包括:
引用变量与对象的区别;
字符串文字"abc"是一个String对象;
文字池[pool of literal strings]和堆[heap]中的字符串对象。
一、引用变量与对象:除了一些早期的Java书籍和现在的垃圾书籍,人们都可以从中比较清楚地学习到两者的区别。A aa;语句声明一个类A的引用变量aa[我常常称之为句柄],而对象一般通过new创建。所以题目中s仅仅是一个引用变量,它不是对象。[ref 句柄、引用与对象]
二、Java中所有的字符串文字[字符串常量]都是一个String的对象。有人[特别是C程序员]在一些场合喜欢把字符串"当作/看成"字符数组,这也没有办法,因为字符串与字符数组存在一些内在的联系。事实上,它与字符数组是两种完全不同的对象。
System.out.println("Hello".length());
char[] cc={'H','i'};
System.out.println(cc.length);
三、字符串对象的创建:由于字符串对象的大量使用[它是一个对象,一般而言对象总是在heap分配内存],Java中为了节省内存空间和运行时间[如比较字符串时,==比equals()快],在编译阶段就把所有的字符串文字放到一个文字池[pool of literal strings]中,而运行时文字池成为常量池的一部分。文字池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。我们知道,对两个引用变量,使用==判断它们的值[引用]是否相等,即指向同一个对象:
String s1 = "abc" ;
String s2 = "abc" ;
if( s1 == s2 )
System.out.println("s1,s2 refer to the same object");
else System.out.println("trouble");
这里的输出显示,两个字符串文字保存为一个对象。就是说,上面的代码只在pool中创建了一个String对象。
现在看String s = new String("abc");语句,这里"abc"本身就是pool中的一个对象,而在运行时执行new String()时,将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。
String s1 = new String("abc") ;
String s2 = new String("abc") ;
if( s1 == s2 ){ //不会执行的语句}
这时用==判断就可知,虽然两个对象的"内容"相同[equals()判断],但两个引用变量所持有的引用不同,
BTW:上面的代码创建了几个String Object? [三个,pool中一个,heap中2个。]
[Java2 认证考试学习指南 (第4版)( 英文版)p197-199有图解。]
2、字符串的+运算和字符串转换
字符串转换和串接是很基础的内容,因此我以为这个问题简直就是送分题。事实上,我自己就答错了。
String str = new String("jf"); // jf是接分
str = 1+2+str+3+4;
一共创建了多少String的对象?[我开始的答案:5个。jf、new、3jf、3jf3、3jf34]
首先看JLS的有关论述:
一、字符串转换的环境[JLS 5.4 String Conversion]
字符串转换环境仅仅指使用双元的+运算符的情况,其中一个操作数是一个String对象。在这一特定情形下,另一操作数转换成String,表达式的结果是这两个String的串接。
二、串接运算符[JLS 15.18.1 String Concatenation Operator + ]
如果一个操作数/表达式是String类型,则另一个操作数在运行时转换成一个String对象,并两者串接。此时,任何类型都可以转换成String。[这里,我漏掉了"3"和"4"]
如果是基本数据类型,则如同首先转换成其包装类对象,如int x视为转换成Integer(x)。
现在就全部统一到引用类型向String的转换了。这种转换如同[as if]调用该对象的无参数toString方法。[如果是null则转换成"null"]。因为toString方法在Object中定义,故所有的类都有该方法,而且Boolean, Character, Integer, Long, Float, Double, and String改写了该方法。
关于+是串接还是加法,由操作数决定。1+2+str+3+4 就很容易知道是"3jf34"。[BTW :在JLS的15.18.1.3中举的一个jocular little example,真的很无趣。]
下面的例子测试了改写toString方法的情况.。
class A{
int i = 10;
public static void main(String []args){
String str = new String("jf");
str += new A();
System.out.print(str);
}
public String toString(){
return " a.i ="+i+"\n";
}
}
三、字符串转换的优化
按照上述说法,str = 1+2+str+3+4;语句似乎应该就应该生成5个String对象:
1+2 =3,then 3→Integer(3)→"3" in pool? [假设如此]
"3"+str(in heap) = "3jf" (in heap)
"3jf" +3 ,first 3→Integer(3)→"3" in pool? [则不创建] then "3jf3"
"3jf3"+4 create "4" in pool
then "3jf34"
这里我并不清楚3、4转换成字符串后是否在池中,所以上述结果仍然是猜测。
为了减少创建中间过渡性的字符串对象,提高反复进行串接运算时的性能,a Java compiler可以使用StringBuffer或者类似的技术,或者把转换与串接合并成一步。例如:对于 a + b + c ,Java编译器就可以将它视为[as if]
new StringBuffer().append(a).append(b).append(c).toString();
注意,对于基本类型和引用类型,在append(a)过程中仍然要先将参数转换,从这个观点看,str = 1+2+str+3+4;创建的字符串可能是"3"、"4"和"3jf34"[以及一个StringBuffer对象]。
现在我仍然不知道怎么回答str = 1+2+str+3+4;创建了多少String的对象,。或许,这个问题不需要过于研究,至少SCJP不会考它。
3、这又不同:str = "3"+"jf"+"3"+"4";
如果是一个完全由字符串文字组成的表达式,则在编译时,已经被优化而不会在运行时创建中间字符串。测试代码如下:
String str1 ="3jf34";
String str2 ="3"+"jf"+"3"+"4";
if(str1 == str2) {
System.out.println("str1 == str2");
}else {
System.out.println("think again");
}
if(str2.equals(str1))
System.out.println("yet str2.equals(str1)");
可见,str1与str2指向同一个对象,这个对象在pool中。所有遵循Java Language Spec的编译器都必须在编译时对constant expressions 进行简化。JLS规定:Strings computed by constant expressions (ý15.28) are computed at compile time and then treated as if they were literals.
对于String str2 ="3"+"jf"+"3"+"4";我们说仅仅创建一个对象。注意,“创建多少对象”的讨论是说运行时创建多少对象。
BTW:编译时优化
String x = "aaa " + "bbb ";
if (false) {
x = x + "ccc ";
}
x += "ddd ";
等价于:
String x = "aaa bbb ";
x = x + "ddd ";
4、不变类
String对象是不可改变的(immutable)。有人对str = 1+2+str+3+4;语句提出疑问,怎么str的内容可以改变?其实仍然是因为不清楚:引用变量与对象的区别。str仅仅是引用变量,它的值——它持有的引用可以改变。你不停地创建新对象,我就不断地改变指向。[参考TIJ的Read-only classes。]
不变类的关键是,对于对象的所有操作都不可能改变原来的对象[只要需要,就返回一个改变了的新对象]。这就保证了对象不可改变。为什么要将一个类设计成不变类?有一个OOD设计的原则:Law of Demeter。其广义解读是:
使用不变类。只要有可能,类应当设计为不变类。
1、tomcat下配置虚拟目录
打开TOMCAT文件下的conf\server.xml文件 ,查找到<ContextManager>标签,并在该标签的结束标签</ContextManager>前面加上:
<Context path="虚拟目录" docBase="硬盘目录" debug="0" reloadable="true" crossContext="true"/>
其中path的值是虚拟目录,docbase的值是你的硬盘的的目录的绝对路径。
如找不到<ContextManager>元素,可以找
<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
然后添加<Context path="虚拟目录" docBase="硬盘目录" debug="0" reloadable="true" crossContext="true"/>
2、禁止tomcat目录浏览,将listings设为false
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>
org.apache.catalina.servlets.DefaultServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
3、设置友好错误页面,配置web.xml
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
4、在IE中直接打开其他扩展名的文件
为了让能在IE浏览器中自动打开其他扩展文件名的文件的设置:
需要在WEB.XML中进行如下的设置:
在WEB.XML中添加<mime-mapping>,其中:
<extension>: 文件的扩展名
<mime-type>: 除了该类型文件的可执行文件,同WINDOW注册表中的 /HKEY_CLASSES_ROOT下该类文件的Content Type 的值一样.
如能在IE中自动打开DOC,XLS,PDF文件的配置如下:
<?xml version="1.0" ?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 1.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<mime-mapping>
<extension>doc</extension>
<mime-type>application/msword</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xls</extension>
<mime-type>application/msexcel</mime-type>
</mime-mapping>
<mime-mapping>
<extension>pdf</extension>
<mime-type>application/pdf</mime-type>
</mime-mapping>
</web-app>
- 确保运行 SQL Server 的服务器上的 Oracle 客户端软件已达到提供程序所要求的级别。用于 Oracle 的 Microsoft OLE DB 提供程序要求 Oracle 客户端软件支持文件的版本为 7.3.3.4.0 或更高版本,并且 SQL*Net 的版本为 2.3.3.0.4。
- 在运行 SQL Server 的服务器上创建指向 Oracle 数据库实例的 SQL*Net 别名。有关更多信息,请参见 Oracle 文档。
- 执行 sp_addlinkedserver 创建链接服务器,指定 MSDAORA 为 provider_name,指定用于 Oracle 数据库实例的 SQL*Net 别名为 data_ source。
以下示例假设已将一个 SQL*Net 别名定义为 OracleDB。
sp_addlinkedserver 'OrclDB', 'Oracle', 'MSDAORA', 'OracleDB'
- 使用 sp_addlinkedsrvlogin 创建从 SQL Server 登录到 Oracle 登录的登录映射。
以下示例通过 Oracle 登录名 OrclUsr 和密码 OrclPwd 将 SQL Server 登录 Joe 映射到步骤 3 中定义的链接服务器:
sp_addlinkedsrvlogin 'OrclDB', false, 'Joe', 'OrclUsr', 'OrclPwd'
每个 Oracle 数据库实例仅有一个名称为空的目录。Oracle 链接服务器中的表必须使用四部分名称格式 OracleLinkedServerName..OwnerUserName.TableName 进行引用。例如,以下 SELECT 语句引用 Oracle 用户 MARY 在 OrclDB 链接服务器映射的服务器上所拥有的表 SALES。
SELECT *
FROM OrclDB..MARY.SALES
注意需要 修改 注册表 :对于win2k/oracle 8i ,修改的内容为:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="oraclient8.dll"
"OracleSqlLib"="orasql8.dll"
"OracleOciLib"="oci.dll"
其它事项:启动mstdc服务
说明:
建议在刚安装完的时候进行修改,对已经投产的系统,风险太大!
步骤如下:
1. Make sure the parallel_server parameter in INIT.ORA is set to false
or it is not set at all.
2. Execute the following commands in Server Manager (svrmgrl):
SVRMGR> SHUTDOWN IMMEDIATE; -- or NORMAL
SVRMGR> STARTUP MOUNT;
SVRMGR> ALTER SYSTEM ENABLE RESTRICTED SESSION;
SVRMGR> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
SVRMGR> ALTER SYSTEM SET AQ_TM_PROCESSES=0;
SVRMGR> ALTER DATABASE OPEN;
SVRMGR> ALTER DATABASE [NATIONAL] CHARACTER SET INTERNAL_USE ;
SVRMGR> SHUTDOWN IMMEDIATE; -- OR NORMAL
SVRMGR> STARTUP RESTRICT;
3. Restore the parallel_server parameter in INIT.ORA, if necessary.
4. Execute the following commands in Server Manager:
SVRMGR> SHUTDOWN IMMEDIATE; -- OR NORMAL
SVRMGR> STARTUP;
如果没有登陆,首先执行
SVRMGR> connect internal;
软件环境:
1、Windows 2000+ORACLE
2、ORACLE安装路径为:C:\ORACLE
实现方法:
1、 开始->设置->控制面板->管理工具->服务停止所有Oracle服务。
2、 开始->程序->Oracle - OraHome81->Oracle Installation Products->Universal Installer卸装所有Oracle产品,但Universal Installer本身不能被删除
5、 运行regedit,选择HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,按del键删除这个入口。
6、 运行regedit,选择HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,滚动这个列表,删除所有Oracle入口。
7、 运行refedit,HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application,
删除所有Oracle入口。
8、 开始->设置->控制面板->系统->高级->环境变量删除环境变量CLASSPATH和PATH中有关Oracle的设定
9、 从桌面上、STARTUP(启动)组、程序菜单中,删除所有有关Oracle的组和图标
10、 删除\Program Files\Oracle目录
11、 重新启动计算机,重起后才能完全删除Oracle所在目录
12、 删除与Oracle有关的文件,选择Oracle所在的缺省目录C:\Oracle,删除这个入口目录及所有子目录,并从C:\WINNT下删除以下文件ORACLE.INI、oradim73.INI、oradim80.INI、oraodbc.ini等等。
13、 WIN.INI文件中若有[ORACLE]的标记段,删除该段
14、 如有必要,删除所有Oracle相关的ODBC的DSN
15、 到事件查看器中,删除Oracle相关的日志
说明:
如果有个别DLL文件无法删除的情况,则不用理会,重新启动,开始新的安装,安装时,选择一个新的目录,则,安装完毕并重新启动后,老的目录及文件就可以删除掉了。
pro*c and vc++ error
From the oracle metalink -
Problem Description
-------------------
You are trying to compile a precompiled Pro*C application.
You are using Microsoft Visual C++ 6.0. At compile time,
you get the error message:
C2079: 'SQLSTM" uses undefined struct 'SQLEXD'
Solution Description
--------------------
In Visual C++, under Project -> Settings -> Precompiled Headers,
set this to "Automatic Use of Precompiled Headers", with the "Through
Header"
option left blank.
HTH.,
Ora600
"Glen"
wrote in message
news:69867261.0110312021.3ba2f3ee@posting.google.com...
> Hi,
>
> I have precompiled some pro*c code which worked fine and outputted a
> c++ file which is included in my project. I have included the
> orasql8.lib and when I try to compile the project receive the
> following error:
>
> error C2079: 'sqlstm' uses undefined struct 'sqlexd'
>
> I don't understand why, the following code appears before where the
> error is encountered.
>
> static struct sqlexd {
> unsigned int sqlvsn;
> unsigned int arrsiz;
> unsigned int iters;
> unsigned int offset;
> unsigned short selerr;
> unsigned short sqlety;
> unsigned int occurs;
> const short *cud;
> unsigned char *sqlest;
> const char *stmt;
> sqladts *sqladtp;
> sqltdss *sqltdsp;
> void **sqphsv;
> unsigned int *sqphsl;
> int *sqphss;
> void **sqpind;
> int *sqpins;
> unsigned int *sqparm;
> unsigned int **sqparc;
> unsigned short *sqpadto;
> unsigned short *sqptdso;
> void *sqhstv[4];
> unsigned int sqhstl[4];
> int sqhsts[4];
> void *sqindv[4];
> int sqinds[4];
> unsigned int sqharm[4];
> unsigned int *sqharc[4];
> unsigned short sqadto[4];
> unsigned short sqtdso[4];
> } sqlstm = {10,4};
>
> If anyone has any ideas i would be most appreciative.
>
> Cheers
> Glen