一曲新词酒一杯,去年天气旧亭台,夕阳西下几时回? 无可奈何花落去,似曾相识燕归来,小园香径独徘徊。

发布新日志

  • Tomcat集群与负载均衡(转载)

    fish_yy 发布于 2007-04-24 12:11:06

            在单一的服务器上执行WEB应用程序有一些重大的问题,当网站成功建成并开始接受大量请求时,单一服务器终究无法满足需要处理的负荷量,所以就有点显得有点力不从心了。另外一个常见的问题是会产生单点故障,如果该服务器坏掉,那么网站就立刻无法运作了。不论是因为要有较佳的扩充性还是容错能力,我们都会想在一台以上的服务器计算机上执行WEB应用程序。所以,这时候我们就需要用到集群这一门技术了。

             在进入集群系统架构探讨之前,先定义一些专门术语:
    1. 集群(Cluster):是一组独立的计算机系统构成一个松耦合的多处理器系统,它们之间通过网络实现进程间的通信。应用程序可以通过网络共享内存进行消息传送,实现分布式计算机。 
    2. 负载均衡(Load Balance):先得从集群讲起,集群就是一组连在一起的计算机,从外部看它是一个系统,各节点可以是不同的操作系统或不同硬件构成的计算机。如一个提供Web服务的集群,对外界来看是一个大Web服务器。不过集群的节点也可以单独提供服务。
    3. 特点:在现有网络结构之上,负载均衡提供了一种廉价有效的方法扩展服务器带宽和增加吞吐量,加强网络数据处理能力,提高网络的灵活性和可用性。集群系统(Cluster)主要解决下面几个问题: 
    高可靠性(HA):利用集群管理软件,当主服务器故障时,备份服务器能够自动接管主服务器的工作,并及时切换过去,以实现对用户的不间断服务。
    高性能计算(HP):即充分利用集群中的每一台计算机的资源,实现复杂运算的并行处理,通常用于科学计算领域,比如基因分析,化学分析等。 
    负载平衡:即把负载压力根据某种算法合理分配到集群中的每一台计算机上,以减轻主服务器的压力,降低对主服务器的硬件和软件要求。

    目前比较常用的负载均衡技术主要有: 
      1. 基于DNS的负载均衡 
      通过DNS服务中的随机名字解析来实现负载均衡,在DNS服务器中,可以为多个不同的地址配置同一个名字,而最终查询这个名字的客户机将在解析这个名字时得到其中一个地址。因此,对于同一个名字,不同的客户机会得到不同的地址,他们也就访问不同地址上的Web服务器,从而达到负载均衡的目的。 

      2. 反向代理负载均衡 (如Apache+JK2+Tomcat这种组合)
      使用代理服务器可以将请求转发给内部的Web服务器,让代理服务器将请求均匀地转发给多台内部Web服务器之一上,从而达到负载均衡的目的。这种代理方式与普通的代理方式有所不同,标准代理方式是客户使用代理访问多个外部Web服务器,而这种代理方式是多个客户使用它访问内部Web服务器,因此也被称为反向代理模式。

      3. 基于NAT(Network Address Translation)的负载均衡技术 (如Linux Virtual Server,简称LVS)
      网络地址转换为在内部地址和外部地址之间进行转换,以便具备内部地址的计算机能访问外部网络,而当外部网络中的计算机访问地址转换网关拥有的某一外部地址时,地址转换网关能将其转发到一个映射的内部地址上。因此如果地址转换网关能将每个连接均匀转换为不同的内部服务器地址,此后外部网络中的计算机就各自与自己转换得到的地址上服务器进行通信,从而达到负载分担的目的。

    介绍完上面的集群技术之后,下面就基于Tomcat的集群架构方案进行说明:

    上面是采用了Apache httpd作为web服务器的,即作为Tomcat的前端处理器,根据具体情况而定,有些情况下是不需要Apache httpd作为 web 服务器的,如系统展现没有静态页面那就不需要Apache httpd,那时可以直接使用Tomcat作为web 服务器来使用。使用Apache httpd主要是它在处理静态页面方面的能力比Tomcat强多了。
    1、 用户的网页浏览器做完本地 DNS和企业授权的DNS之的请求/响应后,这时候企业授权的DNS(即21cn BOSS DNS)会给用户本地的DNS服务器提供一个NAT请求分配器(即网关)IP。


    2、 NAT分配器,它会根据特定的分配算法,来决定要将连接交给哪一台内部 Apache httpd来处理请求。大多数的NAT请求分配器提供了容错能力:根据侦测各种WEB服务器的失效状况,停止将请求分配给已经宕掉的服务器。并且有些分配器还可以监测到WEB服务器机器的负载情况,并将请求分配给负载最轻的服务器等等。Linux Virtual Server是一个基于Linux操作系统上执行的VS-NAT开源软件套件,而且它有丰富的功能和良好的说明文件。商业硬件解决方案 Foundry Networks的ServerIron是目前业界公认最佳的请求分配器之一。


    3、 Apache httpd + Mod_JK2在这里是作为负载均衡器,那为什么要做集群呢?如果集群系统要具备容错能力,以便在任何单一的硬件或软件组件失效时还能100%可用,那么集群系统必须没有单点故障之忧。所以,不能只架设一台有mod_jk2的Apache httpd,因为如果 httpd或mod_jk2失效了,将不会再有请求被会送交到任何一个Tomcat 实例。这种情况下,Apache httpd就是瓶劲,特别在访问量大的网站。


    4、 Mod_JK2负载均衡与故障复原,决定把Apache httpd当成web服务器,而且使用mod_jk2将请求传送给Tomcat,则可以使用mod_jk2的负载均衡与容错功能。在集群系统中,带有mod_jk2的Apache httpd可以做的事情包括:
    A、 将请求分配至一或多个Tomcat实例上
    你可以在mod_jk2的workers.properties文件中,设定许多Tomcat实例,并赋于每个实例一个lb_factor值,以作为请求分配的加权因子。


    B、 侦测Tomcat实例是否失败
    当Tomcat实例的连接器服务不再响应时,mod_jk2会及时侦测到,并停止将请求送给它。其他的Tomcat实例则会接受失效实例的负载。


    C、 侦测Tomcat实例在失效后的何时恢复
    因连接器服务失效,而停止将请求分配给Tomcat实例之后,mod_jk2会周期性地检查是否已恢复使用性,并自动将其加入现行的Tomcat实例池中。


    5、 Tomcat中的集群原理是通过组播的方式进行节点的查找并使用TCP连接进行会话的复制。这里提示一下就是,对每个请求的处理,Tomcat都会进行会话复制,复制后的会话将会慢慢变得庞大。


    6、 Mod_jk2同时支持会话亲和和会话复制。在tomcat 5中如何实现会话亲和和会话复制?把server.xml中的标签去掉就实现会话亲和,把标签加上就实现会话复制。


    7、 会话亲和:就是表示来自同会话的所有请求都由相同的Tomcat 实例来处理,这种情况下,如果Tomcat实例或所执行的服务器机器失效,也会丧失Servlet的会话数据。即使在集群系统中执行更多的Tomcat实例,也永远不会复制会话数据。这样是提高集群性能的一种方案,但不具备有容错能力了。


    8、 使用会话复制,则当一个Tomcat实例宕掉时,由于至少还有另一个Tomcat实例保有一份会话状态数据,因而数据不会丧失。但性能会有所降低。  

    测试者家园 2006-08-29 21:48 发表评论


    Link URL: http://www.cnblogs.com/tester2test/archive/2006/08/29/489830.html
  • 十天学会php之二

    紫忧 发布于 2007-04-25 15:02:55

    学习目的:掌握php的流程控制

    1、if..else 循环有三种结构

    第一种是只有用到 if 条件,当作单纯的判断。解释成 "若发生了某事则怎样处理"。语法如下:

    if (expr) { statement }

    其中的 expr 为判断的条件,通常都是用逻辑运算符号当判断的条件。而 statement 为符合条件的执行部分程序,若程序只有一行,可以省略大括号 {}。

    范例:本例省略大括号。

    <?php
    if ($state==1)echo "哈哈" ;
    ?>

    这里特别注意的是,判断是否相等是==而不是=,ASP程序员可能常犯这个错误,= 是赋值。

    范例:本例的执行部分有三行,不可省略大括号。

    <?php
    if ($state==1) {
    echo "哈哈 ;
    echo "<br>" ;
    }
    ?>


    第两种是除了 if 之外,加上了 else 的条件,可解释成 "若发生了某事则怎样处理,否则该如何解决"。语法如下

    if (expr) { statement1 } else { statement2 } 范例:上面的例子来修改成更完整的处理。其中的 else 由于只有一行执行的指令,因此不用加上大括号。 本文来自 444p.com
    <?php
    if ($state==1) {
    echo "哈哈" ;
    echo "<br>";
    }
    else{
    echo "呵呵";
    echo "<br>";
    }
    ?>


    第三种就是递归的 if..else 循环,通常用在多种决策判断时。它将数个 if..else 拿来合并运用处理。

    直接看下面的例子

    <?php
    if ( $a > $b ) {
    echo "a 比 b 大" ;
    } elseif ( $a == $b ) {
    echo "a 等于 b" ;
    } else {
    echo "a 比 b 小" ;
    }
    ?>

    上例只用二层的 if..else 循环,用来比较 a 和 b 两个变量。实际要使用这种递归 if..else 循环时,请小心使用,因为太多层的循环容易使设计的逻辑出问题,或者少打了大括号等,都会造成程序出现莫名其妙的问题。

    2、 for 循环就单纯只有一种,没有变化,它的语法如下

    for (expr1; expr2; expr3) { statement } 444p.com学习之家

    其中的 expr1 为条件的初始值。expr2 为判断的条件,通常都是用逻辑运算符号 (logical operators) 当判断的条件。expr3 为执行 statement 后要执行的部份,用来改变条件,供下次的循环判断,如加一..等等。而 statement 为符合条件的执行部分程序,若程序只有一行,可以省略大括号 {}。

    下例是用 for 循环写的的例子。

    <?php
    for ( $i = 1 ; $i <= 10 ; $i ) {
    echo "这是第".$i."次循环<br>" ;
    }
    ?>

    3、 switch 循环,通常处理复合式的条件判断,每个子条件,都是 case 指令部分。在实作上若使用许多类似的 if 指令,可以将它综合成 switch 循环。

    语法如下

    switch (expr) { case expr1: statement1; break; case expr2: statement2; break; default: statementN; break; }

    其中的 expr 条件,通常为变量名称。而 case 后的 exprN,通常表示变量值。冒号后则为符合该条件要执行的部分。注意要用 break 跳离循环。

    <?php
    switch ( date ( "D" )) {

    444p.com学习之家


    case "Mon" :
    echo "今天星期一" ;
    break;
    case "Tue" :
    echo "今天星期二" ;
    break;
    case "Wed" :
    echo "今天星期三" ;
    break;
    case "Thu" :
    echo "今天星期四" ;
    break;
    case "Fri" :
    echo "今天星期五" ;
    break;
    default:
    echo "今天放假" ;
    break;
    }
    ?>

    这里需要注意的是break;别遗漏了,default,省略是可以的。

    很明显的,上述的例子用 if 循环就很麻烦了。当然在设计时,要将出现机率最大的条件放在最前面,最少出现的条件放在最后面,可以增加程序的执行效率。上例由于每天出现的机率相同,所以不用注意条件的顺序。

  • 十天学会php之一

    紫忧 发布于 2007-04-25 14:38:14

    下面简单介绍一下PHP的语法。

    本文来自 444p.com

    1、嵌入方法: www.444p.com

    类似ASP的<%,PHP可以是<?php或者是<?,结束符号是?>,当然您也可以自己指定。

    2、引用文件:

    引用文件的方法有两种:require 及 include。
    require 的使用方法如 require(MyRequireFile.php); 。这个函数通常放在 PHP 程序的最前面,PHP 程序在执行前,就会先读入 require 所指定引入的文件,使它变成 PHP 程序网页的一部份。常用的函数,亦可以这个方法将它引入网页中。
    php学习之家http://444p.com

    include 使用方法如 include(MyIncludeFile.php); 。这个函数一般是放在流程控制的处理部分中。PHP 程序网页在读到 include 的文件时,才将它读进来。这种方式,可以把程序执行时的流程简单化。 本文来自 4 44 p.c om

    3、注释方法:

    <?php
    echo 这是第一种例子。n ;
    // 本例是 C 语法的注释
    /* 本例采用多行的
    注释方式 */
    echo 这是第二种例子。n ;  本文来自 4 44 p.
    c om


    echo 这是第三种例子。n ;
    # 本例使用 UNIX Shell 语法注释
    ?>

    PHP学习之家

    4、变量类型: php学习之家

    $mystring = 我是字符串 ;
    $NewLine = 换行了\n ;
    $int1 = 38 ;
    $float1 = 1.732 ;
    $float2 = 1.4E 2 ;
    $MyArray1 = array( 子 , 丑 , 寅 , 卯 ); 本文来自 444p.com

    这里引出两个问题,首先PHP变量以$开头,第二PHP语句以;结尾,可能ASP程序员会不适应。这两个遗漏也是程序上大多错误所在。

    本文来自 4 44 p.c om

    5、运算符号:

    www.444p.com

    数学运算: 符号 意义
    加法运算
    - 减法运算
    * 乘法运算
    / 除法运算
    % 取余数
    累加
    -- 递减 PHP学习之家

    字符串运算: www.444p.com

    运算符号只有一个,就是英文的句号。它可以将字符串连接起来,变成合并的新字符串。类似ASP中的&

    <?
    $a = PHP 4
    ;
    $b = 功能强大
    ;
    echo $a.$b
    ;
    ?>

    这里也引出两个问题,首先PHP中输出语句是echo,第二类似ASP中的<%=变量%>,PHP中也可以<?=变量? >。 php学习之家http://444p.com

    逻辑运算: 4 4 4p.com版权所有

    符号 意义
    < 小于
    > 大于
    <= 小于或等于
    >= 大于或等于
    == 等于
    != 不等于
    && 而且 (And)
    and 而且 (And)
    || 或者 (Or)
    or 或者 (Or)
    xor 异或 (Xor)
    ! 不 (Not) PHP学习之家

  • (转)大规模网站性能优化方法-从LiveJournal后台发展看大规模网站性能优化方法

    ireneyao 发布于 2007-04-27 14:22:05

    大规模网站性能优化方法-从LiveJournal后台发展看大规模网站性能优化方法

    原文地址:http://www.cdnunion.com/cdninfo/cdnunion_2006_11_02_123.html

    因为最近在研究集群问题,转进这篇文章方便研究,原文作者如有意见请在文后留言。

    一、LiveJournal发展历程

    LiveJournal是99年始于校园中的项目,几个人出于爱好做了这样一个应用,以实现以下功能:

    • 博客,论坛
    • 社会性网络,找到朋友
    • 聚合,把朋友的文章聚合在一起

    LiveJournal采用了大量的开源软件,甚至它本身也是一个开源软件。

    在上线后,LiveJournal实现了非常快速的增长:

    • 2004年4月份:280万注册用户。
    • 2005年4月份:680万注册用户。
    • 2005年8月份:790万注册用户。
    • 达到了每秒钟上千次的页面请求及处理。
    • 使用了大量MySQL服务器。
    • 使用了大量通用组件。

    二、LiveJournal架构现状概况

    livejournal_backend.png

    三、从LiveJournal发展中学习

    LiveJournal从1台服务器发展到100台服务器,这其中经历了无数的伤痛,但同时也摸索出了解决这些问题的方法,通过对LiveJournal的学习,可以让我们避免LJ曾经犯过的错误,并且从一开始就对系统进行良好的设计,以避免后期的痛苦。

    下面我们一步一步看LJ发展的脚步。

    1、一台服务器

    一台别人捐助的服务器,LJ最初就跑在上面,就像Google开始时候用的破服务器一样,值得我们尊敬。这个阶段,LJ的人以惊人的速度熟悉的Unix的操作管理,服务器性能出现过问题,不过还好,可以通过一些小修小改应付过去。在这个阶段里LJ把CGI升级到了FastCGI。

    最终问题出现了,网站越来越慢,已经无法通过优过化来解决的地步,需要更多的服务器,这时LJ开始提供付费服务,可能是想通过这些钱来购买新的服务器,以解决当时的困境。
    毫无疑问,当时LJ存在巨大的单点问题,所有的东西都在那台服务器的铁皮盒子里装着。

    LJ-backend-7.png

    2、两台服务器

    用付费服务赚来的钱LJ买了两台服务器:一台叫做Kenny的Dell 6U机器用于提供Web服务,一台叫做Cartman的Dell 6U服务器用于提供数据库服务。

    LJ-backend-8.png

    LJ有了更大的磁盘,更多的计算资源。但同时网络结构还是非常简单,每台机器两块网卡,Cartman通过内网为Kenny提供MySQL数据库服务。

    暂时解决了负载的问题,新的问题又出现了:

    • 原来的一个单点变成了两个单点。
    • 没有冷备份或热备份。
    • 网站速度慢的问题又开始出现了,没办法,增长太快了。
    • Web服务器上CPU达到上限,需要更多的Web服务器。

    3、四台服务器

    又买了两台,Kyle和Stan,这次都是1U的,都用于提供Web服务。目前LJ一共有3台Web服务器和一台数据库服务器。这时需要在3台Web服务器上进行负载均横。

    LJ-backend-9.png

    LJ把Kenny用于外部的网关,使用mod_backhand进行负载均横。

    然后问题又出现了:

    • 单点故障。数据库和用于做网关的Web服务器都是单点,一旦任何一台机器出现问题将导致所有服务不可用。虽然用于做网关的Web服务器可以通过保持心跳同步迅速切换,但还是无法解决数据库的单点,LJ当时也没做这个。
    • 网站又变慢了,这次是因为IO和数据库的问题,问题是怎么往应用里面添加数据库呢?

    4、五台服务器

    又买了一台数据库服务器。在两台数据库服务器上使用了数据库同步(Mysql支持的Master-Slave模式),写操作全部针对主数据库(通过Binlog,主服务器上的写操作可以迅速同步到从服务器上),读操作在两个数据库上同时进行(也算是负载均横的一种吧)。

    LJ-backend-10.png

    实现同步时要注意几个事项:

    • 读操作数据库选择算法处理,要选一个当前负载轻一点的数据库。
    • 在从数据库服务器上只能进行读操作
    • 准备好应对同步过程中的延迟,处理不好可能会导致数据库同步的中断。只需要对写操作进行判断即可,读操作不存在同步问题。

    5、更多服务器

    有钱了,当然要多买些服务器。部署后快了没多久,又开始慢了。这次有更多的Web服务器,更多的数据库服务器,存在 IO与CPU争用。于是采用了BIG-IP作为负载均衡解决方案。

    LJ-backend-11.png

    6、现在我们在哪里:

    LJ-backend-1.png

    现在服务器基本上够了,但性能还是有问题,原因出在架构上。

    数据库的架构是最大的问题。由于增加的数据库都是以Slave模式添加到应用内,这样唯一的好处就是将读操作分布到了多台机器,但这样带来的后果就是写操作被大量分发,每台机器都要执行,服务器越多,浪费就越大,随着写操作的增加,用于服务读操作的资源越来越少。

    LJ-backend-2.png

    由一台分布到两台

    LJ-backend-3.png

    最终效果

    现在我们发现,我们并不需要把这些数据在如此多的服务器上都保留一份。服务器上已经做了RAID,数据库也进行了备份,这么多的备份完全是对资源的浪费,属于冗余极端过度。那为什么不把数据分布存储呢?

    问题发现了,开始考虑如何解决。现在要做的就是把不同用户的数据分布到不同的服务器上进行存储,以实现数据的分布式存储,让每台机器只为相对固定的用户服务,以实现平行的架构和良好的可扩展性。

    为了实现用户分组,我们需要为每一个用户分配一个组标记,用于标记此用户的数据存放在哪一组数据库服务器中。每组数据库由一个master及几个slave组成,并且slave的数量在2-3台,以实现系统资源的最合理分配,既保证数据读操作分布,又避免数据过度冗余以及同步操作对系统资源的过度消耗。

    LJ-backend-4.png

    由一台(一组)中心服务器提供用户分组控制。所有用户的分组信息都存储在这台机器上,所有针对用户的操作需要先查询这台机器得到用户的组号,然后再到相应的数据库组中获取数据。

    这样的用户架构与目前LJ的架构已经很相像了。

    在具体的实现时需要注意几个问题:

    • 在数据库组内不要使用自增ID,以便于以后在数据库组之间迁移用户,以实现更合理的I/O,磁盘空间及负载分布。
    • 将userid,postid存储在全局服务器上,可以使用自增,数据库组中的相应值必须以全局服务器上的值为准。全局服务器上使用事务型数据库InnoDB。
    • 在数据库组之间迁移用户时要万分小心,当迁移时用户不能有写操作。

    7、现在我们在哪里

    LJ-backend-5.png

    问题:

    • 一个全局主服务器,挂掉的话所有用户注册及写操作就挂掉。
    • 每个数据库组一个主服务器,挂掉的话这组用户的写操作就挂掉。
    • 数据库组从服务器挂掉的话会导致其它服务器负载过大。
    • 对于Master-Slave模式的单点问题,LJ采取了Master-Master模式来解决。所谓Master-Master实际上是人工实现的,并不是由MySQL直接提供的,实际上也就是两台机器同时是Master,也同时是Slave,互相同步。

    Master-Master实现时需要注意:

    • 一个Master出错后恢复同步,最好由服务器自动完成。
    • 数字分配,由于同时在两台机器上写,有些ID可能会冲突。

    解决方案:

    • 奇偶数分配ID,一台机器上写奇数,一台机器上写偶数
    • 通过全局服务器进行分配(LJ采用的做法)。

    Master-Master模式还有一种用法,这种方法与前一种相比,仍然保持两台机器的同步,但只有一台机器提供服务(读和写),在每天晚上的时候进行轮换,或者出现问题的时候进行切换。

    8、现在我们在哪里

    LJ-backend-6.png

    现在插播一条广告,MyISAM VS InnoDB。

    使用InnoDB:

    • 支持事务
    • 需要做更多的配置,不过值得,可以更安全的存储数据,以及得到更快的速度。

    使用MyISAM:

    • 记录日志(LJ用它来记网络访问日志)
    • 存储只读静态数据,足够快。
    • 并发性很差,无法同时读写数据(添加数据可以)
    • MySQL非正常关闭或死机时会导致索引错误,需要使用myisamchk修复,而且当访问量大时出现非常频繁。

    9、缓存

    去年我写过一篇文章介绍memcached,它就是由LJ的团队开发的一款缓存工具,以key-value的方式将数据存储到分布的内存中。LJ缓存的数据:

    • 12台独立服务器(不是捐赚的)
    • 28个实例
    • 30GB总容量
    • 90-93%的命中率(用过squid的人可能知道,squid内存加磁盘的命中率大概在70-80%)

    如何建立缓存策略?

    想缓存所有的东西?那是不可能的,我们只需要缓存已经或者可能导致系统瓶颈的地方,最大程度的提交系统运行效率。通过对MySQL的日志的分析我们可以找到缓存的对象。

    缓存的缺点?

    • 没有完美的事物,缓存也有缺点:
    • 增大开发量,需要针对缓存处理编写特殊的代码。
    • 管理难度增加,需要更多人参与系统维护。
    • 当然大内存也需要钱。

    10、Web访问负载均衡

    在数据包级别使用BIG-IP,但BIG-IP并不知道我们内部的处理机制,无法判断由哪台服务器对这些请求进行处理。反向代理并不能很好的起到作用,不是已经够快了,就是达不到我们想要的效果。

    所以,LJ又开发了Perlbal。特点:

    • 快,小,可管理的http web 服务器/代理
    • 可以在内部进行转发
    • 使用Perl开发
    • 单线程,异步,基于事件,使用epoll , kqueue
    • 支持Console管理与http远程管理,支持动态配置加载
    • 多种模式:web服务器,反向代理,插件
    • 支持插件:GIF/PNG互换?

    11、MogileFS

    LJ使用开源的MogileFS作为分布式文件存储系统。MogileFS使用非常简单,它的主要设计思想是:

    • 文件属于类(类是最小的复制单位)
    • 跟踪文件存储位置
    • 在不同主机上存储
    • 使用MySQL集群统一存储分布信息
    • 大容易廉价磁盘

    到目前为止就这么多了,更多文档可以在http://www.danga.com/words/找到。Danga.comLiveJournal.com的同学们拿这个文档参加了两次MySQL Con,两次OS Con,以及众多的其它会议,无私的把他们的经验分享出来,值得我们学习。在web2.0时代快速开发得到大家越来越多的重视,但良好的设计仍是每一个应用的基础,希望web2.0们在成长为Top500网站的路上,不要因为架构阻碍了网站的发展。

    参考资料:http://www.danga.com/words/2005_oscon/oscon-2005.pdf

  • 泛解析,二级域名转向问题- -

    紫忧 发布于 2007-04-26 11:14:42

                                         

    关于实现商务网站二级域名的应用`!

    实现条件:
    1、必须有一个顶级域名,而且此域名必须做好泛解析并做好指向。
    2、必须有一独立的web服务器 。泛解析的域名指向该服务器。
    3、在web服务器上 建一个空的主机头名的web站点。
    4、做域名转向

    什么是域名泛解析?
           客户的顶级域名abc.com,之下所设的*.abc.com全部解析到同一个IP地址上去。比如客户设b.abc.com就会自已自动解析到与abc.com同一个IP地址上去,显示的是跟abc.com一样的页面。
    目的是让用户可以注册abc.com为后缀虚拟三级域名系统(也可以称为免费域名)。

    实现:
           进入DNS
           打开要做泛解析的域 (abc.com)
           建立新域 名字为 *
           进入这个域建立一个空主机 只输入 服务器的IP
          OK~!

    实现了泛解析就表示以后 *.abc.com的访问都会到 指定的WEB服务器上去,现在要做的就是做域名转向

    <!-- #include file="conn/conn.asp" -->
    <%
    tURL = Request.ServerVariables("HTTP_HOST")        '请求页面的地址
    dURL = left(turl,instr(tURL,".")-1)                               '二级域名名字
    sURL = dURL&"."
    sURL = replace(tURL,""&sURL&"","")                         '顶级域名,也即abc.com

    If dURL="www" or durl="" then
        Response.Redirect("默认首页")
        Response.End()
    Else
       Set rs = conn.execute("select id,username from [register] where username='"&dURL&"'")
       If   rs.eof and rs.bof then
           response.Redirect("默认首页")
           response.End()
       Else
        url= ""       '此处url 为二级域名访问的路径
         rs.close
        Set rs = nothing
    %>


    <HTML>
    <HEAD>
    <META http-equiv="Content-Type" content="text/html; charset=gb2312">
    <META CONTENT="text/html; CHARSET=UTF-8" HTTP-EQUIV="Content-Type">
    <TITLE>LD160兰德网</TITLE>
    </HEAD>
    <frameset frameborder="0" framespacing="0" scrolling="no" border="0" marginheight="0" marginwidth="0" rows="0,*">
    <frame scrolling="NO" noresize="0" marginwidth="0" marginheight="0" framespacing="0" frameborder="0" target="main" name="main1" SRC="about:blank">

    <frame scrolling="yes" noresize="0" marginwidth="0" marginheight="0" framespacing="0" frameborder="0" target="main1" name="main1" SRC="<%=url%>">

    <noframes>
    <body>
    <p>This page uses frames, but your browser doesn't support them.</p></body>
    </noframes>
    </frameset>
    </HTML>
    <%
       end if
       end if
    %>

    把上面的代码存为index.asp放在WEB服务器的默认站点上就ok

    关键的地方就是frameset 框架的使用,目的是让地址栏始终显示的是二级域名地址。

  • 揭开Socket编程的面纱(转载)

    annayin 发布于 2007-04-26 10:00:59

    TCP/IPUDPSocket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问:
    A9~^c!NPGuest
    yI$F0c5i"R.J$VGuest
    1.         什么是TCP/IPUDP
    @*X8?6eI*~1_Guest
    2.         Socket在哪里呢?QSC休闲博客1WI;`^K9f6T
    3.         Socket是什么呢?
    ~\-C??tiGuest
    4.         你会使用它们吗?QSC休闲博客7tx9Kt `m&w

    -e-z y6Y4e ijCGuest
    什么是TCP/IPUDP

             TCP/IPTransmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
    _n@%ePZ7rGuest
             UDPUser Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
    ktGMheGuest
            这里有一张图,表明了这些协议的关系。
    J6sUX(J b,b-zGuest
    T2A\9L2H'oZGuest                                                                               
    b_Ypcy/`GuestQSC休闲博客mWp lc7I*I
                                                                            图1

           TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IPUDP的关系了吧。QSC休闲博客b^_Y Hc2`c
    Socket在哪里呢?
    2G wpt9w*l6W\mGuest
           在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。


    /g m[w(lGuestQSC休闲博客8d|[ p)g:mO
    2

           原来Socket在这里。QSC休闲博客p9o2T!t9v'hrL
    Socket是什么呢?QSC休闲博客 BO^Nt
           Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。QSC休闲博客 gG g+C K
    你会使用它们吗?QSC休闲博客N{r gJ/D V w
           前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。QSC休闲博客v@hn%v&^8t
           一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

          

    3

           先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。QSC休闲博客.z:N*p4P*lOD g
           在这里我就举个简单的例子,我们走的是TCP协议这条路(见图2)。例子用MFC编写,运行的界面如下:

    QSC休闲博客`1^"i$A)E{+\L
    QSC休闲博客7k:K yvuW'S}N
    4

    QSC休闲博客E(k'@(gnf
    QSC休闲博客 @'q wp0v&m4^
    5

           在客户端输入服务器端的IP地址和发送的数据,然后按发送按钮,服务器端接收到数据,然后回应客户端。客户端读取回应的数据,显示在界面上。QSC休闲博客%v xu;q!PL ?
           下面是接收数据和发送数据的函数:

    int    Receive(SOCKET fd,char *szText,int len)

    {QSC休闲博客@+L^\3IKN
           int cnt;QSC休闲博客V5^'|-L5J8x"LB
           int rc;
    )uXp?.SJGuest
           cnt=len;

           while(cnt>0)
    d \`$D%|)X7ruest
           {
    @%WnLsiGuest
                  rc=recv(fd,szText,cnt,0);QSC休闲博客+^@jWW(Y
                  if(rc==SOCKET_ERROR)
    Hg9d+F rs N$NGuest
                  {
    .Qu!G7oY7h6t+mGuest
                         return -1;QSC休闲博客X2Q+K0V-QDh|~
                 }

                 if(rc==0)

                         return len-cnt;

                  szText+=rc;

                  cnt-=rc;

           }

           return len;

    }QSC休闲博客G@:u.\u;o
    QSC休闲博客~[u"[U5I+hC7c'H
    int Send(SOCKET fd,char *szText,int len)QSC休闲博客8J,c!q&{ t+L y
    {

           int cnt;

           int rc;

           cnt=len;

           while(cnt>0)

           {

                  rc=send(fd,szText,cnt,0);

                  if(rc==SOCKET_ERROR)

                  {

                         return -1;

                  }

                  if(rc==0)

                         return len-cnt;

                  szText+=rc;

                  cnt-=rc;

           }

           return len;

    }

    服务器端:

           在服务器端,主要是启动Socket和监听线程。

    #define DEFAULT_PORT      2000

    void CServerDlg::OnStart()

    {

           sockaddr_in local;

           DWORD dwThreadID = 0;

          

           local.sin_family=AF_INET;

           //设置的端口为DEFAULT_PORT

           local.sin_port=htons(DEFAULT_PORT);

           //IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。

           local.sin_addr.S_un.S_addr=INADDR_ANY;

     

           //初始化Socket

           m_Listening = socket(AF_INET,SOCK_STREAM,0);

           if(m_Listening == INVALID_SOCKET)

           {

                  return ;

           }

           //将本地地址绑定到所创建的套接字上

           if(bind(m_Listening,(LPSOCKADDR)&local,sizeof(local)) == SOCKET_ERROR )

           {

                  closesocket(m_Listening);

                  return ;

           }

           //创建监听线程,这样也能响应界面上操作。

           m_hListenThread = ::CreateThread(NULL,0,ListenThread,this,0,&dwThreadID);

           m_StartBtn.EnableWindow(FALSE);

           m_StopBtn.EnableWindow(TRUE);

    }QSC休闲博客7Dp})a8YM*wWm/u
    QSC休闲博客7gO CBJK j6w
    监听线程函数:
    6}^ ?,M!_Guest
    DWORD WINAPI CServerDlg::ListenThread(LPVOID lpparam)
    ] S"fe&W&G QL*NGuest
    {

           CServerDlg* pDlg = (CServerDlg*)lpparam;

           if(pDlg == NULL)

                  return 0;

     

           SOCKET  Listening = pDlg->m_Listening;

           //开始监听是否有客户端连接。

           if(listen(Listening,40) == SOCKET_ERROR)

           {

                  return 0;

           }

           char szBuf[MAX_PATH];

           //初始化

           memset(szBuf,0,MAX_PATH);

           while(1)

           {

                  SOCKET ConnectSocket;

                  sockaddr_in    ClientAddr;

                  int                  nLen = sizeof(sockaddr);

                  //阻塞直到有客户端连接,不然多浪费CPU资源。

                  ConnectSocket = accept(Listening,(sockaddr*)&ClientAddr,&nLen);

                  //都到客户端的IP地址。

                  char *pAddrname = inet_ntoa(ClientAddr.sin_addr);

                  pDlg->Receive(ConnectSocket,szBuf,100);

                  //界面上显示请求数据。

                  pDlg->SetRequestText(szBuf);

                  strcat(szBuf," :我是老猫,收到(");

                  strcat(szBuf,pAddrname);

                  strcat(szBuf,"");

                  //向客户端发送回应数据

                  pDlg->Send(ConnectSocket,szBuf,100);

           }

           return 0;

    }

           服务器端一直在监听是否有客户端连接,如有连接,处理客户端的请求,给出回应,然后继续监听。

    客户端:

           客户端的发送函数:

    #define DEFAULT_PORT      2000

    void CClientDlg::OnSend()

    {

           DWORD dwIP = 0;      

           TCHAR szText[MAX_PATH];

           memset(szText,0,MAX_PATH);

           m_IP.GetWindowText(szText,MAX_PATH);

           //把字符串形式的IP地址转成IN_ADDR结构需要的形式。

           dwIP = inet_addr(szText);

           m_RequestEdit.GetWindowText(szText,MAX_PATH);

     

           sockaddr_in local;

           SOCKET socketTmp;

           //必须是AF_INET,表示该socketInternet域中进行通信

           local.sin_family=AF_INET;

           //端口号

           local.sin_port=htons(DEFAULT_PORT);

           //服务器的IP地址。

           local.sin_addr.S_un.S_addr=dwIP;

          

           ////初始化Socket

           socketTmp=socket(AF_INET,SOCK_STREAM,0);

           //连接服务器

           if(connect(socketTmp,(LPSOCKADDR)&local,sizeof(local)) < 0)

           {

                  closesocket(socketTmp);

                  MessageBox("连接服务器失败。");

                  return ;

           }

           //发送请求,为简单只发100字节,在服务器端也规定100字节。

           Send(socketTmp,szText,100);

           //读取服务器端返回的数据。

           memset(szText,0,MAX_PATH);

           //接收服务器端的回应。

           Receive(socketTmp,szText,100);

     

           TCHAR szMessage[MAX_PATH];

           memset(szMessage,0,MAX_PATH);

           strcat(szMessage,szText);

           //界面上显示回应数据。

           m_ReplyBtn.SetWindowText(szMessage);

           closesocket(socketTmp);

    }

           客户端就一个函数完成了一次通信。在这里IP地址为何用127.0.0.1呢?使用这个IP地址,服务器端和客户端就能运行在同一台机器上,这样调试方便多了。当然你可以在你朋友的机器上运行Server程序(本人在局域网中测试过),在自己的机器上运行Client程序,当然输入的IP地址就该是你朋友机器的IP地址了。QSC休闲博客yG-KZ,|1Q1O-_2R6I
           简单的理论和实践都说了,现在Socket编程不神秘了吧?希望对你有些帮助。  

  • 2006-12-31 | 防火墙相关知识

    caicai1724 发布于 2007-04-27 17:46:35

    防火墙是个比较热的词,大家平时也肯定或多或少的接触到了这个词,我们力求在防火墙这个专题中给予大家更多的关于防火墙的知识,在这里我们所指的防火墙是Internet的防火墙。包括个人的和企业的。所谓“病毒防火墙”不是我们这次讨论的范畴,因为其本身只是杀毒软件的一个新功能。

       Firewall 防火墙一种确保网络安全的方法。防火墙可以被安全放置在一个单独的路由器中,用来过滤不想要的信息包,也可以被安装在路由器和主机中,发挥更大的网络安全保护作用。防火墙被广泛用来让用户在一个安全屏障后接入互联网,还被用来把一家企业的公共网络服务器和企业内部网络隔开。另外,防火墙还可以被用来保护企业内部网络某一个部分的安全。例如,一个研究或者会计子网可能很容易受到来自企业内部网络里面的窥探。

      简单说防火墙就是一个位于计算机和它所连接的网络之间的软件。该计算机流入流出的所有网络通信均要经过此防火墙。

        随着计算机网络技术的突飞猛进,网络安全的问题已经日益突出地摆在各类用户的面前。仅从笔者掌握的资料表明,目前在互联网上大约有将近20%以上的用户曾经遭受过黑客的困扰。尽管黑客如此猖獗,但网络安全问题至今仍没有能够引起足够的重视,更多的用户认为网络安全问题离自己尚远,这一点从大约有40%以上的用户特别是企业级用户没有安装防火墙(Firewall)便可以窥见一斑,而所有的问题都在向大家证明一个事实,大多数的黑客入侵事件都是由于未能正确安装防火墙而引发的。

      防火墙的概念及作用

      防火墙的本义原是指古代人们房屋之间修建的那道墙,这道墙可以防止火灾发生的时候蔓延到别的房屋。而这里所说的防火墙当然不是指物理上的防火墙,而是指隔离在本地网络与外界网络之间的一道防御系统,是这一类防范措施的总称。应该说,在互联网上防火墙是一种非常有效的网络安全模型,通过它可以隔离风险区域(即Internet或有一定风险的网络)与安全区域(局域网)的连接,同时不会妨碍人们对风险区域的访问。防火墙可以监控进出网络的通信量,从而完成看似不可能的任务;仅让安全、核准了的信息进入,同时又抵制对企业构成威胁的数据。随着安全性问题上的失误和缺陷越来越普遍,对网络的入侵不仅来自高超的攻击手段,也有可能来自配置上的低级错误或不合适的口令选择。因此,防火墙的作用是防止不希望的、未授权的通信进出被保护的网络,迫使单位强化自己的网络安全政策。一般的防火墙都可以达到以下目的:一是可以限制他人进入内部网络,过滤掉不安全服务和非法用户;二是防止入侵者接近你的防御设施;三是限定用户访问特殊站点;四是为监视Internet安全提供方便。由于防火墙假设了网络边界和服务,因此更适合于相对独立的网络,例如Intranet等种类相对集中的网络。防火墙正在成为控制对网络系统访问的非常流行的方法。事实上,在Internet上的Web网站中,超过三分之一的Web网站都是由某种形式的防火墙加以保护,这是对黑客防范最严,安全性较强的一种方式,任何关键性的服务器,都建议放在防火墙之后。

      防火墙的架构与工作方式

      防火墙可以使用户的网络划规划更加清晰明了,全面防止跨越权限的数据访问(因为有些人登录后的第一件事就是试图超越权限限制)。如果没有防火墙的话,你可能会接到许许多多类似的报告,比如单位内部的财政报告刚刚被数万个Email邮件炸烂,或者用户的个人主页被人恶意连接向了Playboy,而报告链接上却指定了另一家色情网站......一套完整的防火墙系统通常是由屏蔽路由器和代理服务器组成。屏蔽路由器是一个多端口的IP路由器,它通过对每一个到来的IP包依据组规则进行检查来判断是否对之进行转发。屏蔽路由器从包头取得信息,例如协议号、收发报文的IP地址和端口号、连接标志以至另外一些IP选项,对IP包进行过滤。代理服务器是防火墙中的一个服务器进程,它能够代替网络用户完成特定的TCP/TP功能。一个代理服务器本质上是一个应用层的网关,一个为特定网络应用而连接两个网络的网关。用户就一项TCP/TP应用,比如Telnet或者FTP,同代理服务器打交道,代理服务器要求用户提供其要访问的远程主机名。当用户答复并提供了正确的用户身份及认证信息后,代理服务器连通远程主机,为两个通信点充当中继。整个过程可以对胜户完全透明。用户提供的用户身份及认证信息可用于用户级的认证。最简单的情况是:它只由用户标识和口令构成。但是,如果防火墙是通过Internet可访问的,应推荐用户使用更强的认证机制,例如一次性口令或回应式系统等。

    屏蔽路由器的最大优点就是架构简单且硬件成本较低,而缺点则是建立包过滤规则比较困难,加之屏蔽路由器的管理成本及用户级身份认证的缺乏等。好在路由器生产商们已经认识到并开始着手解决这些问题,他们正在开发编辑包过滤规则的图形用户界面,制订标准的用户级身份认证协议,以提供远程身份认证拨入用户服务(REDIUS)。

      代理服务器的优点在于用户级的身份认证、日志记录和帐号管理。其缺点关系到这样一个事实;要想提供全面的安全保证,就要对每一项服务都建立对应的应用层网关。这个事实严重地限制了新应用的采纲。

      屏蔽路由器和代理服务器通常组合在一起构成混合系统,其中屏蔽路由器主要用来防止IP欺骗攻击。目前采用最广泛的配置是Dualhomed防火墙、被屏蔽主机型防火墙以及被屏蔽子网型防火墙。

      通常架设防火墙需要数千甚至上万美元的投入,而且防火墙需要运行于一台独立的计算机上,因此只用一台计算机连入互联网的用户是不必要架设防火墙的,况且这样做即使从成本方面讲也太不划算。目前观之,防火墙的重点还是用来保护由许多台计算机组成的大型网络,这也是黑客高手们真正感兴趣的地方。防火墙可以是非常简单的过滤器,也可能是精心配置的网关,但它们的原理是一样,都是监测并过滤所有通向外部网和从外部网传来的信息,防火墙保护着内部敏感的数据不被偷窃和破坏,并记下来通讯发生的时间和操作等等,新一代的防火墙甚至可以阻止内部人员故意将敏感数据传输到外界。当用户将单位内部的局部网连入互联网时,大家肯定不愿意让全世界的人随意翻阅你单位内部人员的工资单、各种文件资料或者是数据库,但即使在单位内部也存在数据攻击的可能性。例如一些心怀叵测的电脑高手可能会修改工资表和财务报告。而通过设置防火墙后,管理员就可以限定单位内部员工使用Email、浏览WWW以及文件传输,但不允许外界任意访问单位内部的计算机,同时管理员也可以禁止单位中不同部门之间互相访问。将局部网络放置防火墙之后可以阻止来自外界的攻击。而防火墙通常是运行在一台单独的计算机之上的一个特别的软件,它可以识别并屏蔽非法的请求。例如一台WWW代理服务器,所有的请求都间接地由代理服务器处理,这台服务器不同于普通的代理服务器,它不会直接地处理请求,它会验证请求发出者的身份、请求的目的地和请求内容。如果一切符合要求的话,这个请求会被批准送到真正的WWW服务器上。当真正的WWW服务器处理完这个请求后并不会直接把结果发送给请求者,它会把结果送到代理服务器,代理服务器会按照事先的规定检查这个结果是否违反了安全规定,当这一切都通过后,返回结果才会真正地送到请求者的手里。

      防火墙的体系结构

      1、屏蔽路由器(ScreeningRouter)

      屏蔽路由器可以由厂家专门生产的路由器实现,也可以用主机来实现。屏蔽路由器作为内外连接的惟一通道,要求所有的报文都必须在此通过检查。路由器上可以安装基于IP层的报文过滤软件,实现报文过滤功能。许多路由器本身带有报文过滤配置选项,但一般比较简单。单纯由屏蔽路由器构成的防火墙的危险包括路由器本身及路由器允许访问的主机。屏蔽路由器的缺点是一旦被攻隐后很难发现,而且不能识别不同的用户。

      2、双穴主机网关(DualHomedGateway)

      双穴主机网关是用一台装有两块网卡的堡垒主机的做防火墙。两块网卡各自与受保护网和外部网相连。堡垒主机上运行着防火墙软件,可以转发应用程序,提供服务等。与屏蔽路由器相比,双穴主机网关堡垒主机的系统软件可用于维护护系统日志、硬件拷贝日志或远程日志。但弱点也比较突出,一旦黑客侵入堡垒主机并使其只具有路由功能,任何网上用户均可以随便访问内部网。

    3、被屏蔽主机网关(ScreenedGatewy)

      屏蔽主机网关易于实现也最为安全。一个堡垒主机安装在内部网络上,通常在路由器上设立过滤规则,并使这个堡垒主机成为从外部网络惟一可直接到达的主机,这确保了内部网络不受未被授权的外部用户的攻击。如果受保护网是一个虚拟扩展的本地网,即没有子网和路由器,那么内部网的变化不影响堡垒主机和屏蔽路由器的配置。危险带限制在堡垒主机和屏蔽路由器。网关的基本控制策略由安装在上面的软件决定。如果攻击者没法登录到它上面,内网中的其余主机就会受到很大威胁。这与双穴主机网关受攻击时的情形差不多。

      4、被屏蔽子网(ScreenedSubnet)

      被屏蔽子网就是在内部网络和外部网络之间建立一个被隔离的子网,用两台分组过滤路由器将这一子网分别与内部网络和外部网络分开。在很多实现中,两个分组过滤路由器放在子网的两端,在子网内构成一个DNS,内部网络和外部网络均可访问被屏蔽子网,但禁止它们穿过被屏蔽子网通信。有的屏蔽子网中还设有一堡垒主机作为惟一可访问点,支持终端交互或作为应用网关代理。这种配置的危险仅包括堡垒主机、子网主机及所有连接内网、外网和屏蔽子网的路由器。如果攻击者试图完全破坏防火墙,他必须重新配置连接三个网的路由器,既不切断连接又不要把自己锁在外面,同时又不使自己被发现,这样也还是可能的。但若禁止网络访问路由器或只允许内网中的某些主机访问它,则攻击会变得很困难。在这种情况下,攻击者得先侵入堡垒主机,然后进入内网主机,再返回来破坏屏蔽路由器,并且整个过程中不能引发警报。


      防火墙的基本类型

      如今市场上的防火墙林林总总,形式多样。有以软件形式运行在普通计算机之上的,也有以固件形式设计在路由器之中的。总的来说可以分为三种:包过滤防火墙、代理服务器和状态监视器。

      包过滤防火墙(IPFiltingFirewall):

      包过滤(PacketFilter)是在网络层中对数据包实施有选择的通过,依据系统事先设定好的过滤逻辑,检查数据据流中的每个数据包,根据数据包的源地址、目标地址、以及包所使用端口确定是否允许该类数据包通过。在互联网这样的信息包交换网络上,所有往来的信息都被分割成许许多多一定长度的信息包,包中包括发送者的IP地址和接收者的IP地址。当这些包被送上互联网时,路由器会读取接收者的IP并选择一条物理上的线路发送出去,信息包可能以不同的路线抵达目的地,当所有的包抵达后会在目的地重新组装还原。包过滤式的防火墙会检查所有通过信息包里的IP地址,并按照系统管理员所给定的过滤规则过滤信息包。如果防火墙设定某一IP为危险的话,从这个地址而来的所有信息都会被防火墙屏蔽掉。这种防火墙的用法很多,比如国家有关部门可以通过包过滤防火墙来禁止国内用户去访问那些违反我国有关规定或者"有问题"的国外站点,例如www.playboy.com、www.cnn.com等等。包过滤路由器的最大的优点就是它对于用户来说是透明的,也就是说不需要用户名和密码来登录。这种防火墙速度快而且易于维护,通常做为第一道防线。包过滤路由器的弊端也是很明显的,通常它没有用户的使用记录,这样我们就不能从访问记录中发现黑客的攻击记录。而攻击一个单纯的包过滤式的防炎墙对黑客来说是比较容易的,他们在这一方面已经积了大量的经验。"信息包冲击"是黑客比较常用的一种攻击手段,黑客们对包过滤式防火墙发出一系列信息包,不过这些包中的IP地址已经被替换掉了(FakeIP),取而代之的是一串顺序的IP地址。一旦有一个包通过了防火墙,黑客便可以用这个IP地十来伪装他们发出的信息。在另一些情况下黑客们使用一种他们自己编制的路由器攻击程序,这种程序使用路由器协议(RoutingInformationProtcol)来发送伪造的路由信息,这样所有的包都会被重新路由到一个入侵者所指定的特别地址。对付这种路由器的另一种技术被称之为"同步淹没",这实际上是一种网络炸弹。攻击者向被攻击的计算机发出许许多多个虚假的"同步请求"信号包,当服务器响应了这种信号包后会等待请求发出者的回答,而攻击者不做任何的响应。如果服务器在45秒钟里没有收到反应信号的话就会取消掉这次请求。但是当服务器在处理成知上万个虚假请求时,它便没有时间来处理正常的用户请求,处于这种攻击下的服务器和死锁没什么两样。此种防火墙的缺点是很明显的,通常它没有用户的使用记录,这样我们就不能从访问记录中发现黑客的攻击记录。此外,配置繁琐也是包过滤防火墙的一个缺点。它阻挡别人进入内部网络,但也不告诉你何人进入你的系统,或者何人从内部进入网际网路。它可以阻止外部对私有网络的访问,却不能记录内部的访问。包过滤另一个关键的弱点就是不能在用户级别上进行过滤,即不能鉴别不同的用户和防止IP地址盗用。包过滤型防火墙是某种意义上的绝对安全的系统。

    代理服务器(ProxyServer):

      代理服务器通常也称作应用级防火墙。包过滤防火墙可以按照IP地址来禁止未授权者的访问。但是它不适合单位用来控制内部人员访问外界的网络,对于这样的企业来说应用级防火墙是更好的选择。所谓代理服务,即防火墙内外的计算机系统应用层的链接是在两个终止于代理服务的链接来实现的,这样便成功地实现了防火墙内外计算机系统的隔离。代理服务是设置在Internet防火墙网关上的应用,是在网管员允许下或拒绝的特定的应用程度或者特定服务,同时,还可应用于实施较强的数据流监控、过滤、记录和报告等功能。一般情况下可应用于特定的互联网服务,如超文本传输(HTTP)、远程文件传输(FTP)等。代理服务器通常拥有高速缓存,缓存中存有用户经常访问站点的内容,在下一个用户要访问同样的站点时,服务器就用不着重复地去抓同样的内容,既节约了时间也节约了网络资源。

      下面笔者向网友们简单介绍几种代理服务器的设计实现方式:

      1、应用代理服务器(ApplicationGatewayProxy)

      应用代理服务器可以在网络应用层提供授权检查及代理服务。当外部某台主机试图访问(如Telnet)受保护网时,它必须先在防火墙上经过身份认证。通过身份认证后,防火墙运行一个专门为Telnet设计的程序,把外部主机与内部主机连接。在这个过程中,防火墙可以限制用户访问的主机、访问的时间及访问的方式。同样,受保护网络内部用户访问外部网时也需先登录到防火墙上,通过验证后才可使用Telnet或FTP等有效命令。应用网关代理的优点是既可以隐藏内部IP地址,也可以给单个用户授权,即使攻击者盗用了一个合法的IP地址。他也通不过严格的身份认证。因特网关比报文过滤具有更高的安全性。但是这种认证使得应用网关不透明,用户每次连接都要受到"盘问",这给用户带来许多不便。而且这种代理技术需要为每个应用网关写专门的程序。

      2、回路级代理服务器

      回路级代理服务器也称一般代理服务器,它适用于多个协议,但无法解释应用协议,需要通过其他方式来获得信息。所以,回路级代理服务器通常要求修改过的用户程序。其中,套接字服务器(SocketsServer)就是回路级代理服务器。套接字(Sockets)是一种网络应用层的国际标准。当受保护网络客户机需要与外部网交互信息时,在防火墙上的套接字服务器检查客户的UserID、IP源地址和IP目的地址,经过确认后,套服务器才与外部的段服务器建立连接。对用户来说,受保护网与外部网的信息交换是透明的,感觉不到防火墙的存在,那是因为因特网络用户不需要登录到防火墙上。但是客户端的应用软件必须支持"SocketsifideAPI"受保护网络用户访问公共网所使用的IP地址也都是防火墙的IP地址。

      3、代管服务器

      代管服务器技术换言之就是把不安全的服务,诸如FTP、Telnet等放到防火墙上,使它同时充当服务器,对外部的请求作出回答。与应用层代理实现相比,代管服务器技术不必为每种服务专门写程序。而且,受保护网内部用户想对外部网访问时,也需先登录到防火墙上,再向外提出请求,这样从外部网向内就只能看到防火墙,从而隐藏了内部地址,提高了安全性。

      4、IP通道(IPTunnels)

      如果某公司下属的两个子公司相隔较远,通过Internet进行通信时,可以采用IPTunnels来防止Internet上的黑客截取信息,从而在Internet上形成一个虚构的企业网。

      5、网地址转换器(NetworkAddressTranslate)

      当受保护网连到Internet上时,受保护网用户若要访问Internet,必须使用一个合法的IP地址。但由于合法InternetIP地址有限,而且受保护网络往往有自己的一套IP地址规划。网络地址转换器就是在防火墙上装一个合法IP地址集。当内部某一用户要访问Internet时,防火墙态地从地址集中选一个未分配的地址分配给该用户,该用户即可使用这个合法地址进行通信。同时,对于内部的某些服务器如Web服务器,网络地址转换器允许为其分配一个固定的合法地址。外部网络的用户就可通过防火墙来访问内部的服务器。这种技术既缓解了少量的IP地址和大量的主机之间的矛盾,又对外隐藏了内部主机的IP地址,提高了安全性。

    6、隔离域名服务器(SplitDomainNameSever)

      这种技术是通过防火墙将受保护网络的域名服务器与外部网的域名服务器隔离,使外部网的域名服务器只能看到防火墙的IP地址,无法了解受保护网络的具体情况,这样可以保证受保护网络的IP地址不被外部网络知悉。

      7、邮件转发技术(Mailforwarding)

      当防火墙采用上面所提到的几种技术使得外部网络只知道防火墙的IP地址和域名时,从外部网络发来的邮件,就只能送到防火墙上。这时防火墙对邮件进行检查,只有当发送邮件的源主机是被允许通过的,防火墙才对邮件的目的地址进行转换,送到内部的邮件服务器,由其进行转发。

      代理服务器像真的墙一样挡在内部用户和外界之间,特别是从外面来的访问者只能看到代理服务器而看不见到任何的内部资源,诸如用户的IP等。而内部客户根本感觉不到它的存在,可以自由访问外部站点。代理可以提供极好的访问控制、登录能力以及地址转换功能,对进出防火墙的信息进行记录,便于管理员监视和管理系统。但代理服务器同时也存在一些不足,特别是它会使网络的访问速度变慢,因为它不允许用户直接访问网络,而代理又要处理入和出的通信量,因此每增加一种新的媒体应用,则必须对代理进行设置。笔者在一套办公应用软件的设计方面,就因为代理服务器的原因折腾了很长时间,结果还是由于设置与容错方面的问题暂时搁浅了。

      状态监视器(StatefulInspection):

      状态监视器作为防火墙技术其安全特性最佳,它采用了一个在网关上执行网络安全策略的软件引擎,称之为检测模块。检测模块在不影响网络正常工作的前提下,采用抽取相关数据的方法对网络通信的各层实施监测,抽取部分数据,即状态信息,并动态地保存起来作为以后制定安全决策的参考。检测模块支持多种协议和应用程序,并可以很容易地实现应用和服务的扩充。与其它安全方案不同,当用户访问到达网关的操作系统前,状态监视器要抽取有关数据进行分析,结合网络配置和安全规定作出接纳、拒绝、鉴定或给该通信加密等决定。一旦某个访问违反安全规定,安全报警器就会拒绝该访问,并作下记录向系统管理器报告网络状态。状态监视器的另一个优点就是可以监测RemoteProcedureCall和User DatagrqamProtocol类的端口信息。问题当然也有,即状态监视器的配置非常复杂,而且会降低网络的速度。

      目前防火墙已经在Internet上得到了广泛的应用,而且由于防火墙不限于TCP/IP协议的特点,也使其逐步在Internet之外更具生命力。客观的讲,防火墙并不是解决网络安全问题的万能药方,而只是网络安全政策和策略中的一个组成部分,但了解防火墙技术并学会在实际操作中应用防火墙技术,相信会在新世纪的网络生活中让每一位网友都受益菲浅。

  • 2006-12-31 | 如何学习J2EE【转帖】

    caicai1724 发布于 2007-04-27 17:48:04

    Java发展到现在,按应用来分主要分为三大块:J2SE,J2ME和J2EE。这三块相互补充,应用范围不同。
    J2SE就是Java2的标准版,主要用于桌面应用软件的编程;
    J2ME主要应用于嵌入是系统开发,如手机和PDA的编程;
    J2EE是Java2的企业版,主要用于分布式的网络程序的开发,如电子商务网站和ERP系统。

    先学习j2se
    要学习j2ee就要先学习j2se,刚开始学习j2se先建议不要使用IDE,然后渐渐的过渡到使用IDE开发,毕竟用它方便嘛。学习j2se推荐两本书,《java2核心技术一二卷》,《java编程思想》,《java模式》。其中《java编程思想》要研读,精读。这一段时间是基本功学习,时间会很长,也可能很短,这要看学习者自身水平而定。

    不要被IDE纠缠
    在学习java和j2ee过程中,你会遇到五花八门的IDE,不要被他们迷惑,学JAVA的时候,要学语言本身的东西,不要太在意IDE的附加功能,JAVA编程在不同IDE之间的转换是很容易的,过于的在意IDE的功能反而容易耽误对语言本身的理解。目前流行的IDE有jbuilder,eclipse和eclipse的加强版WSAD。用好其中一个就可以了,推荐从eclipse入手j2ee。因为Jbuilder更适合于写j2se程序。

    选择和学习服务器使用配置
    当你有了j2se和IDE的经验时,可以开始j2ee的学习了,web服务器:tomcat,勿庸置疑,tomcat为学习web服务首选。而应用服务器目前主要有三个:jboss、weblogic、websphere。有很多项目开始采用jboss,并且有大量的公司开始做websphere或weblogic向jboss应用服务器的移植(节省成本),这里要说的是,学习tomcat和jboss我认为是首选,也是最容易上手的。学习服务器使用配置最好去询问有经验的人(有条件的话),因为他们或许一句话就能解决问题,你自己上网摸索可能要一两天(我就干过这种傻事),我们应该把主要时间放在学习原理和理论上,一项特定技术的使用永远代替不了一个人的知识和学问。

    学习web知识
    如果你是在做电子商务网站等时,你可能要充当几个角色,这是你还要学习:
    html,可能要用到dreamwave等IDE。
    Javascrīpt,学会简单的数据校验,数据联动显示等等

    J2eeAPI学习
    学习j2eeAPI和学习服务器应该是一个迭代的过程。
    先学习jsp和servlet编程,这方面的书很多,我建立看oreilly公司的两本《jsp设计》和《java servlet编程》,oreilly出的书总是那本优秀,不得不佩服。
    学习jdbc数据库编程,j2ee项目大多都是MIS系统,访问数据库是核心。这本应属于j2se学习中,这里拿出来强调一下。
    学习jndi api,它和学习ejb可以结合起来。
    学习ejb api,推荐书《精通ejb》
    经过上面的这些的学习,大概可以对付一般的应用了。
    有人说跟着sun公司的《j2ee tutorial》一路学下来,当然也可以。

    学习ejb设计模式和看代码(最重要)
    设计模式是练内功,其重要性可以这么说吧,如果你不会用设计模式的话,你将写出一堆使用了ejb的垃圾,有慢又是一堆bug,其结果不如不用ejb实现(ejb不等于j2ee)
    无论学习什么语言,都应该看大量代码,你看的代码量不到一定数量,是学不好j2ee的
    目前有很多开源的工程可以作为教材:
    jive论坛
    petstore sun公司
    dune sun公司
    等等,研读一个,并把它用到自己的工程中来。

    J2ee其他学习
    当你渐渐对j2ee了解到一定深度时,你要开始关注当前领域中的一些技术变化,J2ee是一块百家争鸣的领域,大家都在这里提出自己的解决方案,例如structs,hiberate,ofbiz等等,学习这些东西要你的项目和目标而定,预先补充一下未尝不可,但不用涉及太深,毕竟学习原理和理论是最最重要的事。

    目前常见j2eeAPI
    JavaServer Pages(JSP)技术1.2
    Java Servlet技术2.3
    JDBC API 2.0
    Java XML处理API(JAXP)1.1
    Enterprise JavaBeans技术2.0
    Java消息服务(JMS)1.0
    Java命名目录接口(JNDI)1.2
    Java事务API(JTA) 1.0
    JavaMail API 1.2
    JavaBeans激活架构(JAF)1.0
    J2EE连接器体系结构(JCA)1.0
    Java认证和授权服务(JAAS)1.0
    学习上面的某些API要以你的项目而定,了解所有他们总之是有好处的
    上面印证了大家说的一句话,java语言本身不难学,但是技术太多,所以学java很费劲。回想一下,基本上每个初学者,在刚学习java的时候可能都会问别人这么一句话,你怎么知道的哪个方法(api)在哪个包里的?呵呵,无他,唯手熟尔。
  • 2006-12-31 | 个人电脑安全设置【转贴】

    caicai1724 发布于 2007-04-27 17:43:15

    谈到个人上网时的安全,还是先把大家可能会遇到的问题归个类吧。我们遇到的入侵方式大概包括了以下几种:

      (1) 被他人盗取密码;

      (2) 系统被木马攻击;

      (3) 浏览网页时被恶意的java scrpit程序攻击;

      (4) QQ被攻击或泄漏信息;

      (5) 病毒感染;

      (6) 系统存在漏洞使他人攻击自己。

      (7) 黑客的恶意攻击。

    下面我们就来看看通过什么样的手段来更有效的防范攻击。

      1.察看本地共享资源

      运行CMD输入net share,如果看到有异常的共享,那么应该关闭。但是有时你关闭共享下次开机的时候又出现了,那么你应该考虑一下,你的机器是否已经被黑客所控制了,或者中了病毒。

      2.删除共享(每次输入一个)

      net share admin$ /delete

      net share c$ /delete

      net share d$ /delete(如果有e,f,……可以继续删除)

      3.删除ipc$空连接

      在运行内输入regedit,在注册表中找到 HKEY-LOCAL_MACHINESYSTEMCurrentControSetControlLSA 项里数值名称RestrictAnonymous的数值数据由0改为1。

      4.关闭自己的139端口,ipc和RPC漏洞存在于此。

      关闭139端口的方法是在“网络和拨号连接”中“本地连接”中选取“Internet协议(TCP/IP)”属性,进入“高级TCP/IP设置”“WinS设置”里面有一项“禁用TCP/IP的NETBIOS”,打勾就关闭了139端口。

      5.防止rpc漏洞

      打开管理工具——服务——找到RPC(Remote Procedure Call (RPC) Locator)服务——将故障恢复中的第一次失败,第二次失败,后续失败,都设置为不操作。

      XP SP2和2000 pro sp4,均不存在该漏洞。

      6.445端口的关闭

      修改注册表,添加一个键值

      HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NetBT\Parameters在右面的窗口建立一个SMBDeviceEnabled 为REG_DWORD类型键值为 0这样就ok了

      7.3389的关闭

      XP:我的电脑上点右键选属性-->远程,将里面的远程协助和远程桌面两个选项框里的勾去掉。

      Win2000server 开始-->程序-->管理工具-->服务里找到Terminal Services服务项,选中属性选项将启动类型改成手动,并停止该服务。(该方法在XP同样适用)

      使用2000 pro的朋友注意,网络上有很多文章说在Win2000pro 开始-->设置-->控制面板-->管理工具-->服务里找到Terminal Services服务项,选中属性选项将启动类型改成手动,并停止该服务,可以关闭3389,其实在2000pro 中根本不存在Terminal Services。

      8.4899的防范

      网络上有许多关于3389和4899的入侵方法。4899其实是一个远程控制软件所开启的服务端端口,由于这些控制软件功能强大,所以经常被黑客用来控制自己的肉鸡,而且这类软件一般不会被杀毒软件查杀,比后门还要安全。

      4899不象3389那样,是系统自带的服务。需要自己安装,而且需要将服务端上传到入侵的电脑并运行服务,才能达到控制的目的。

      所以只要你的电脑做了基本的安全配置,黑客是很难通过4899来控制你的。

      9、禁用服务

      打开控制面板,进入管理工具——服务,关闭以下服务

      1.Alerter[通知选定的用户和计算机管理警报]

      2.ClipBook[启用“剪贴簿查看器”储存信息并与远程计算机共享]

      3.Distributed File System[将分散的文件共享合并成一个逻辑名称,共享出去,关闭后远程计算机无法访问共享

      4.Distributed Link Tracking Server[适用局域网分布式链接? ?踪客户端服务]

      5.Human Interface Device Access[启用对人体学接口设备(HID)的通用输入访问]

      6.IMAPI CD-Burning COM Service[管理 CD 录制]

      7.Indexing Service[提供本地或远程计算机上文件的索引内容和属性,泄露信息]

      8.Kerberos Key Distribution Center[授权协议登录网络]

      9.License Logging[监视IIS和SQL如果你没安装IIS和SQL的话就停止]

      10.Messenger[警报]

      11.NetMeeting Remote Desktop Sharing[netmeeting公司留下的客户信息收集]

      12.Network DDE[为在同一台计算机或不同计算机上运行的程序提供动态数据交换]

      13.Network DDE DSDM[管理动态数据交换 (DDE) 网络共享]

      14.Print Spooler[打印机服务,没有打印机就禁止吧]

      15.Remote Desktop Help& nbsp;Session Manager[管理并控制远程协助]

      16.Remote Registry[使远程计算机用户修改本地注册表]

      17.Routing and Remote Access[在局域网和广域往提供路由服务.黑客理由路由服务刺探注册信息]

      18.Server[支持此计算机通过网络的文件、打印、和命名管道共享]

      19.Special Administration Console Helper[允许管理员使用紧急管理服务远程访问命令行提示符]

      20.TCP/IPNetBIOS Helper[提供 TCP/IP 服务上的 NetBIOS 和网络上客户端的 NetBIOS 名称解析的支持而使用户能够共享文件、打印和登录到网络]

      21.Telnet[允许远程用户登录到此计算机并运行程序]

      22.Terminal Services[允许用户以交互方式连接到远程计算机]

      23.Window s Image Acquisition (WIA)[照相服务,应用与数码摄象机]

      如果发现机器开启了一些很奇怪的服务,如r_server这样的服务,必须马上停止该服务,因为这完全有可能是黑客使用控制程序的服务端。

      10、账号密码的安全原则

      首先禁用guest帐号,将系统内建的administrator帐号改名~~(改的越复杂越好,最好改成中文的),而且要设置一个密码,最好是8位以上字母数字符号组合。 (让那些该死的黑客慢慢猜去吧~)

      如果你使用的是其他帐号,最好不要将其加进administrators,如果加入administrators组,一定也要设置一个足够安全的密码,同上如果你设置adminstrator的密码时,最好在安全模式下设置,因为经我研究发现,在系统中拥有最高权限的帐号,不是正常登陆下的adminitrator帐号,因为即使有了这个帐号,同样可以登陆安全模式,将sam文件删除,从而更改系统的administrator的密码!而在安全模式下设置的administrator则不会出现这种情况,因为不知道这个administrator密码是无法进入安全模式。权限达到最大这个是密码策略:用户可以根据自己的习惯设置密码,下面是我建议的设置(关于密码安全设置,我上面已经讲了,这里不再罗嗦了。    

      打开管理工具.本地安全设置.密码策略

      1.密码必须符合复杂要求性.启用

      2.密码最小值.我设置的是8

      3.密码最长使用期限.我是默认设置42天

      4.密码最短使用期限0天

      5.强制密码历史 记住0个密码

      6.用可还原的加密来存储密码 禁用    

      11、本地策略:

      这个很重要,可以帮助我们发现那些心存叵测的人的一举一动,还可以帮助我们将来追查黑客。

      (虽然一般黑客都会在走时会清除他在你电脑中留下的痕迹,不过也有一些不小心的)

      打开管理工具    

      找到本地安全设置.本地策略.审核策略

      1.审核策略更改 成功失败

      2.审核登陆事件 成功失败

      3.审核对象访问 失败

      4.审核跟踪过程 无审核

      5.审核目录服务访问 失败

      6.审核特权使用 失败

      7.审核系统事件 成功失败

      8.审核帐户登陆时间 成功失败

      9.审核帐户管理 成功失败

      &nb sp;然后再到管理工具找到

      事件查看器

      应用程序:右键>属性>设置日志大小上限,我设置了50mb,选择不覆盖事件

      安全性:右键>属性>设置日志大小上限,我也是设置了50mb,选择不覆盖事件

      系统:右键>属性>设置日志大小上限,我都是设置了50mb,选择不覆盖事件

      12、本地安全策略:

      打开管理工具    

      找到本地安全设置.本地策略.安全选项      

      1.交互式登陆.不需要按 Ctrl+Alt+Del 启用 [根据个人需要,? 但是我个人是不需要直接输入密码登陆的]

      2.网络访问.不允许SAM帐户的匿名枚举 启用

      3.网络访问.可匿名的共享 将后面的值删除

      4.网络访问.可匿名的命名管道 将后面的值删除

      5.网络访问.可远程访问的注册表路径 将后面的值删除

      6.网络访问.可远程访问的注册表的子路径 将后面的值删除

      7.网络访问.限制匿名访问命名管道和共享

      8.帐户.(前面已经详细讲过拉 )

      13、用户权限分配策略:

      打开管理工具    

      找到本地安全设置.本地策略.用户权限分配      

      1.从网络访问计算机 里面一般默认有5个用户,除Admin外我们删除4个,当然,等下我们还得建一个属于自己的ID

      2.从远程系统强制关机,Admin帐户也删除,一个都不留    

      3.拒绝从网络访问这台计算机 将ID删除

      4.从网络访问此计算机,Admin也可删除,如果你不使用类似3389服务

      5.通过远端强制关机。删掉

      14、终端服务配置

      打开管理工具    

      终端服务配置

      1.打开后,点连接,右键,属性,远程控制,点不允许远程控制

      2.常规,加密级别,高,在使用标准Windows验证上点√!

      3.网卡,将最多连接数上设置为0

      4.高级,将里面的权限也删除.[我没设置]

      再点服务器设置,在Active Desktop上,设置禁用,且限制每个使用一个会话

     
     
    15、用户和组策略

      打开管理工具

      计算机管理.本地用户和组.用户;

      删除Support_388945a0用户等等

      只留下你更改好名字的adminisrator权限  

      计算机管理.本地用户和组.组      

      组.我们就不分组了,每必要把

      16、自己动手DIY在本地策略的安全选项      

      1)当登陆时间用完时自动注销用户(本地) 防止黑客密码渗透.

      2)登陆屏幕上不显示上次登陆名(远程)如果开放3389服务,别人登陆时,就不会残留有你登陆的用户名.让他去猜你的用户名去吧.

      3)对匿名连接的额外限制

      4)禁止按 alt+crtl +del(没必要)

      5)允许在未登陆前关机[防止远程关机/启动、强制关机/启动]

      6)只有本地登陆用户才能访问cd-rom

      7)只有本地登陆用户才能访问软驱

      8)取消关机原因的提示

      A、打开控制面板窗口,双击“电源选项”图标,在随后出现的电源属性窗口中,进入到“高级”标签页面;

      B、在该页面的“电源按钮”设置项处,将“在按下计算机电源按钮时”设置为“关机”,单击“确定”按钮,来退出设置框;

      C、以后需要关机时,可以直接按下电源按键,就能直接关闭计算机了。当然,我们也能启用休眠功能键,来实现快速关机和开机;

      D4、要是系统中没有启用休眠模式的话,可以在控制面板窗口中,打开电源选项,进入到休眠标签页面,并在其中将“启用休眠”选项选中就可以了。

      9)禁止关机事件跟踪

      开始“Start ->”运行“ Run ->输入”gpedit.msc “,在出现的窗口的左边部分,选择 ”计算机配置“(Computer Configuration )-> ”管理模板“(Administrative Templates)-> ”系统“(System),在右边窗口双击“Shutdown Event Tracker” 在出现的对话框中选择“禁止”(Disabled),点击然后“确定”(OK)保存后退出这样,你将看到类似于Windows 2000的关机窗口









          



     17、常见端口的介绍              

      TCP

      21   FTP

      22   SSH

      23   TELNET

      25   TCP SMTP

      53   TCP DNS

      80   HTTP

      135  epmap

      138  [冲击波]

      139  smb

      445

      1025 DCE/1ff70682-0a51-30e8-076d-740be8cee98b

      1026 DCE/12345778-1234-abcd-ef00-0123456789ac

      1433 TCP SQL SERVER

      5631 TCP PCANYWHERE

      5632 UDP PCANYWHERE

      3389   Terminal Services

      4444[冲击波]   

      UDP

      67[冲击波]

      137 netbios-ns

      161 An SNMP Agent is running/ Default community names of the SNMP Agent

      关于UDP一般只有腾讯QQ会打开4000或者是8000端口或者8080,那么,我们只运 行本机使用4000这几个端口就行了

      18、另外介绍一下如何查看本机打开的端口和tcp\ip端口的过滤

      开始--运行--cmd

      输入命令netstat -a

      会看到例如(这是我的机器开放的端口)  

      Proto Local Address    Foreign Address    State

      TCP  yf001:epmap     yf001:0       LISTE

      TCP  yf001:1025(端口号)      yf001:0       LISTE

      TCP  (用户名)yf001:1035      yf001:0       LISTE

      TCP  yf001:netbios-ssn   yf001:0       LISTE

      UDP  yf001:1129      *:*

      UDP  yf001:1183      *:*

      UDP  yf001:1396      *:*

      UDP  yf001:1464      *:*

      UDP  yf001:1466      *:*

      UDP  yf001:4000      *:*

      UDP  yf001:4002      *:*

      UDP  yf001:6000      *:*

      UDP  yf001:6001      *:*

      UDP  yf001:6002      *:*

      UDP  yf001:6003      *:*

      UDP  yf001:6004      *:*

      UDP  yf001:6005      *:*

      UDP  yf001:6006      *:*

      UDP  yf001:6007      *:*

      UDP  yf001:1030      *:*

      UDP  yf001:1048      *:*

      UDP  yf001:1144      *:*

      UDP  yf001:1226      *:*

      UDP  yf001:1390      *:*

      UDP  yf001:netbios-ns   *:*

      UDP  yf001:netbios-dgm   *:*

      UDP  yf001:isakmp     *:*

      现在讲讲基于Windows的tcp/ip的过滤

      控制面板——网络和拨号连接——本地连接——INTERNET协议(tcp/ip)--属性--高级---选项-tcp/ip筛选--属性!!

      然后添加需要的tcp 和UDP端口就可以了~如果对端口不是很了解的话,不要轻易进行过滤,不然可能会导致一些程序无法使用。

      19、胡言乱语

      (1)、TT浏览器

      选择用另外一款浏览器浏览网站.我推荐用TT,使用TT是有道理的。

      TT可以识别网页中的脚本,JAVA程序,可以很好的抵御一些恶意的脚本等等,而且TT即使被感染,你删除掉又重新安装一个就是。

      MYIE浏览器

      是一款非常出色的浏览器,篇幅有险,不做具体介绍了。(建议使用)

      (2)、移动“我的文档”

      进入资源管理器,右击“我的文档”,选择“属性”,在“目标文件夹”选项卡中点“移动”按钮,选择目标盘后按“确定”即可。在Windows 2003 中“我的文档”已难觅芳踪,桌面、开始等处都看不到了,建议经常使用的朋友做个快捷方式放到桌面上。

      (3)、移动IE临时文件

      进入“开始→控制面板→Internet 选项”,在“常规”选项“Internet 文件”栏中点“设置”按钮,在弹出窗体中点“移动文件夹”按钮,选择目标文件夹后,点“确定”,在弹出对话框中选择“是”,系统会自动重新登录。点本地连接>高级>安全日志,把日志的目录更改专门分配日志的目录,不建议是C:再重新分配日志存储值的大小,我是设置了10000KB。

      20、避免被恶意代码 木马等病毒攻击    

      以上主要讲怎样防止黑客的恶意攻击,下面讲避免机器被恶意代码,木马之类的病毒攻击。

      其实方法很简单,所以放在最后讲。

      我们只需要在系统中安装杀毒软件

      如 卡巴基斯,瑞星,金山独霸等

      还有防止木马的木马克星和金山的反木马软件(可选)

      并且能够及时更新你的病毒定义库,定期给你的系统进行全面杀毒。杀毒务必在安全模式下进行,这样才能有效清除电脑内的病毒以及驻留在系统的非法文件。

      还有就是一定要给自己的系统及时的打上补丁,安装最新的升级包。微软的补丁一般会在漏洞发现半个月后发布,而且如果你使用的是中文版的操作系统,那么至少要等一个月的时间才能下到补丁,也就是说这一个月的时间内你的系统因为这个漏洞是很危险的。

      本人强烈建议个人用户安装使用防火墙(目前最有效的方式)

      例如:天网个人防火墙、诺顿防火墙、瑞星防火墙等等。

      因为防火墙具有数据过滤功能,可以有效的过滤掉恶意代码,和阻止DDOS攻击等等。总之如今的防火墙功能强大,连漏洞扫描都有,所以你只要安装防火墙就可以杜绝大多数网络攻击,但是就算是装防火墙也不要以为就万事无忧。因为安全只是相对的,如果哪个邪派高手看上你的机器,防火墙也无济于事。我们只能尽量提高我们的安全系数,尽量把损失减少到最小。

      安全意识也很重要,我们平时上网的时候都应该有一个好的安全意识。加上我们的不懈努力,相信我们的网络生活会更美好。
       

        说这么多希望朋友们自己多学习,多实践,多钻研。尽情的享受网络给我们带来的便利和快捷,只有了解它,才能更好的利用它。

      我坚信只有安全才能自由,只有自由才能快乐。
  • (转)如何掌握Linux系统内存管理

    ireneyao 发布于 2007-04-27 15:50:28

    原文出处:http://www.mcublog.com/blog/user1/11203/archives/2007/20961.html

    事先申明:这个地址没次我点了瑞星都会报有病毒,不要怪我没提醒过哦~~~

    所有转贴的文章都只是我搜集到这里方便研究学习的,如果原文作者有任何意见,请在本文后留言,我一定及时处理

                            如何掌握Linux系统内存管理

    内存是Linux内核所管理的最重要的资源之一,内存管理系统是操作系统中最为重要的部分。对于Linux的初学者来说,熟悉Linux的内存管理非常重要。

      进程是运行于虚拟地址空间的一个程序。可以说,任何在Linux系统下运行的程序都是进程。Linux系统中包括交互进程和批处理进程。交互进程是由Shell控制和运行的,既可以在前台运行,也可以在后台运行。批处理进程不属于某个终端,被提交到一个队列中以便顺序执行。大多数的进程都需要虚拟内存。

      一般需要多少内存

      对于典型的Linux应用系统,128MB内存是合理的选择。如果不运行X-Window系统,那么在一台特殊用途的机器(比如用于调试设备驱动程序的“崩溃和烧毁”系统)上仅用8MB内存就可以工作。

      笔者曾经做过实验,在128MB和256MB下编译内核所需的时间几乎一样,都少于3分半钟(笔者的Linux发行版本是Mandrake Linux 9.1,内核2.4.21)。在一个只有8MB内存的系统上,编译需要的时间会更长一些。类似Web浏览器这样的多媒体应用软件,在内存充足时会运行得更流畅,特别是在一边编译程序,一边上网浏览的时候更是如此。因此,如果只有128MB内存,则预期的性能会有所降低。类似地,如果要开发消耗大量内存的应用程序,可能会要求更多的内存。所以,需要多少内存由工作需求来决定。

      实时监控内存使用情况

      1.在命令行使用“Free”命令监控内存使用情况

    #free
                

    total used free shared buffers cachedMem:

    256024 192284 63740 0

    10676 101004-/+ buffers/cache:

    80604

    75420Swap:

    522072 0 522072


      上面代码给出了一个256MB的RAM和512MB交换空间的系统情况。第三行输出(Mem:)显示物理内存。Total列不显示核心使用的物理内存(通常大约1MB)。Used列显示被使用的内存总额(第二行不计缓冲)。Free列显示全部没有使用的内存。Shared列显示多个进程共享的内存总额。Buffers列显示磁盘缓存的当前大小。第五行(Swap:)对换空间,显示的信息类似上面。如果这行为全0,那么就没有使用对换空间。在缺省的状态下,free命令以千字节(也就是1024字节为单位)来显示内存使用情况。使用-h参数,以字节为单位显内存使用情况;或者使用-m参数,以兆字节为单位显示内存使用情况。还可以通过-s参数,使用命令来不间断地监视内存使用情况:
    #free -b -s5
    这个命令将会在终端窗口中连续不断地报告内存的使用情况,每5秒钟更新一次。

    2.使甩vmstat命令监视虚拟内存使用情况

    #  vmstatprocs -----------
                

    memory---------- ---swap-- -----io----

    --system-- ----cpu----

    r b swpd free buff cache

    si so bi bo in

    cs us sy id wa 1 0

    0 63692 10704 101008

    0 0 239 42 126

    105 48 45 7 0

      vmstat()命令是一个通用监控程序,是Virtual Meomory Statistics(虚拟内存统计)的缩写。如果使用vmstat命令的时候没有使用任何命令行参数,将会得到一个一次性的报告。vmstat命令报告主要的活动类型有进程(procs)、内存(以千字节为单位)、交换分区(以千字节为单位)、来自块设备(硬盘驱动器)的输入输出量、系统中断(每秒钟发生的次数),以及中央处理单元(CPU)分配给用户、系统和空闲时分别占用的比例。

      虚拟内存实现的机制

      存储管理子系统是操作系统中最重要的组成部分之一。在早期计算时代,由于人们所需要的内存数目远远大于物理内存,因此设计出了各种各样的策略来解决此问题,其中最成功的就是虚拟内存技术,它使得系统中有限的物理内存竞争进程所需内存空间得到满足。虚拟内存通过在各个进程之间共享内存,而使系统看起来有多于实际内存的内存容量。Linux支持虚拟内存, 就是使用磁盘作为RAM的扩展,使可用内存相应地有效扩大。核心把当前不用的内存块存到硬盘,腾出内存给其它目的。当原来的内容又要使用时,再读回内存。运行于Linux的程序只看到大量的可用内存,而不关心哪部分在磁盘上。当然,读写硬盘比真的内存慢(大约慢千倍),所以程序运行较慢。用做虚拟内存的这部分硬盘叫对换空间。

      虚拟内存技术不仅仅让我们可以使用更多的内存,它还提供了下面这些功能: 

      1.巨大的寻址空间

      操作系统让系统看上去有比实际内存大得多的内存空间。虚拟内存可以是系统中实际物理空间的许多倍。每个进程运行在其独立的虚拟地址空间中,这些虚拟空间相互之间都完全隔离开来,所以进程间不会互相影响。同时,硬件虚拟内存机构可以将内存的某些区域设置成不可写,这样可以保护代码与数据不会受恶意程序的干扰。 

      2.公平的物理内存分配

      内存管理子系统允许系统中每个运行的进程公平地共享系统中的物理内存。 

    3.共享虚拟内存

      尽管虚拟内存允许进程有其独立的虚拟地址空间,但有时也需要在进程之间共享内存。例如,有可能系统中有几个进程同时运行BASH命令外壳程序。为了避免在每个进程的虚拟内存空间内都存在BASH程序的拷贝,较好的解决办法是系统物理内存中只存在一份BASH的拷贝,并在多个进程间共享。动态库则是另外一种进程间共享执行代码的方式。共享内存可用来作为进程间通信(IPC)的手段,多个进程通过共享内存来交换信息。Linux支持SYSTEM V的共享内存IPC机制。 

      4.进程的保护

      系统中的每一个进程都有自己的虚拟地址空间。这些虚拟地址空间是完全分开的,这样一个进程的运行不会影响其它进程,并且硬件上的虚拟内存机制是被保护的,内存不能被写入。这样可以防止迷失的应用程序覆盖代码的数据。

      5.Linux虚拟内存实现机制

      Linux虚拟内存的实现需要6种机制的支持:地址映射机制、内存分配回收机制、缓存和刷新机制、请求页机制、交换机制和内存共享机制。

      内存管理程序通过映射机制把用户程序的逻辑地址映射到物理地址。当用户程序运行时,如果发现程序中要用的虚地址没有对应的物理内存,就发出了请求页要求。如果有空闲的内存可供分配,就请求分配内存(于是用到了内存的分配和回收),并把正在使用的物理页记录在缓存中(使用了缓存机制)。如果没有足够的内存可供分配,那么就调用交换机制;腾出一部分内存。另外,在地址映射中要通过TLB(翻译后援存储器)来寻找物理页;交换机制中也要用到交换缓存,并且把物理页内容交换到交换文件中,也要修改页表来映射文件地址。Linux虚拟内存实现原理见图1。

                                                                                      图1 Linux虚拟内存实现原理 


      6.虚拟内存容量设定

      也许有人说,虚拟内存容量的设定应该分配2倍于物理内存,但这只是个规律。如果物理内存比较小,可以这样设定。如果有256MB物理内存或更多的话,就可以缩小虚拟内存。Linux会把大量的内存用做Cache,但在资源紧张时会收回。只要看到swap为0,或者该数很小就可以放心了,内存放着不用才是最大的浪费。

      内存泄露和回收内存的方法

      1.内存泄漏的定义

      一般常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的、大小任意的(内存块的大小可以在程序运行期决定)、使用完后必须显示释放的内存。应用程序一般使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块。否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。

      2.内存泄露的危害

    从用使用程序的角度来看,内存泄漏本身不会产生什么危害。作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积。而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。存在内存泄漏问题的程序除了会占用更多的内存外,还会使程序的性能急剧下降。对于服务器而言,如果出现这种情况,即使系统不崩溃,也会严重影响使用。

      3.内存泄露的检测和回收

    对于内存溢出之类的麻烦,大家可能在编写指针比较多的复杂程序时就会遇到。在Linux或Unix下,C和C++语言是最常使用的工具。但是C++程序缺乏相应的手段来检测内存信息,只能使用top指令观察进程的动态内存总额。而且程序退出时,我们无法获知任何内存泄漏信息。

  • 2007-01-06 | DOS下常用命令字[转]

    caicai1724 发布于 2007-04-27 18:05:56

    文件名是由文件路径和文件名称合起来的,如C:\DOS\COMMAND.COM。


    DIR 显示文件和文件夹(目录)。

    用法:DIR [文件名] [选项]


    它有很多选项,如/A表示显示所有文件(即包括带隐含和系统属性的文件),/S表示也显示子文件夹中的文件,/P表示分屏显示,/B表示只显示文件名,等等。

    如 DIR A*.EXE /A /P

    此命令分屏显示当前文件夹下所有以A开头后缀为EXE的文件(夹)。


    CD或CHDIR 改变当前文件夹。

    用法:CD [文件夹名]

    若无文件夹名则显示当前路径。


    MD或MKDIR 建立文件夹。

    用法:MD 文件夹名


    RD或RMDIR 删除文件夹。

    用法:RD 文件夹名

    注意:此文件夹必须是空的。


    DEL或ERASE 删除文件。

    用法:DEL/ERASE 文件名


    COPY 拷贝文件。

    用法: COPY 文件名1 [文件名2] [选项]

    如 COPY /B A+B C

    此命令将两个二进制文件A和B合为一个文件C。


    TYPE 显示文件内容。

    用法:TYPE 文件名


    REN或RENAME 改变文件名,在DOS7中还可以改变文件夹名。

    用法:REN 文件(夹)名1 文件(夹)名2


    EDIT 编辑文件,在DOS7中还可以编辑二进制文件。

    用法:EDIT [文件名] [选项]

    如 EDIT /70 C:\COMMAND.COM

    此命令以二进制方式编辑C:\COMMAND.COM文件。


    FORMAT 格式化磁盘。

    用法:FORMAT 驱动器 [选项]


    它的选项很多,如/Q是快速格式化,/U表示无条件格式化(即无法使用UNFORMAT等命令恢复),/V指定磁盘的卷标名,等等。它还有许多未公开参数。


    MEM 显示内存状态。

    用法:MEM [选项]


    它的选项也有不少,如/C可列出所有程序的内存占用,/D是显示驻留内存的程序及设备驱动程序的状态等详细信息,/F显示空闲的内存总量,/M显示内存中的模块信息,/P则是分屏显示。还有隐藏的/A选项,可以显示HMA信息。


    MOVE 移动文件或文件夹,还可以更改文件或文件夹的名称。

    用法:MOVE 文件[夹]1 文件[夹]2

    如 MOVE C:\*.EXE D:

    此命令可以将C盘根文件夹下所有扩展名为EXE的文件移到D盘上。


    XCOPY 复制文件或文件夹。

    用法:XCOPY 文件[夹]名1 [文件[夹]名2] [选项]


    它的选项非常多,如/S可拷贝整个文件夹(包括子文件夹)中的文件,/E指定包括空文件夹,/V表示复制完后检验复制出的文件的正确性,/Y表示确认,等等。


    CLS 清除屏幕。

    用法:CLS


    SYS 传导系统,即将系统文件(如IO.SYS等)从一处传输到指定的驱动器中。

    用法:SYS 文件夹名 [驱动器]

    如 SYS C:\DOS A:

    此命令即可将位于C:\DOS文件夹下的系统文件传输到A盘中。


    DATE 显示或设置日期。

    用法:DATE [日期]



    TIME 显示或设置时间。

    用法:TIME [时间]



    DOS还自带一些其它的命令,如SORT(排序),FIND(寻找字符)等。


    除DOS自带的命令以外,还有很多其它的增强命令也非常实用,它们可以大大增强DOS的功能。这些软件中有很多可以在“DOS软件”中下载。
  • 2007-01-08 | java的向上转型实例

    caicai1724 发布于 2007-04-27 18:06:52

    下面是一个从网上搜来的有关向上转型的例子,其实我还有些不明白。

    class Fathter2
    { 
      int a = 1;
      int b;
      void f()
      {
      System.out.println("in A");
      }
    }
     
    class Son2 extends Fathter2
    {
      double a = 1.1;
      double b;
      void f()
      {
      System.out.println("in b");
      }
    }
     
    public class ExtendsTest
    {
      public static void main(String[] args)
      {
        Fathter2 aFather2 = new Fathter2();
        Son2 aSon2 = new Son2();
        System.out.println(aFather2.a);  
        aFather2 = aSon2; //疑惑1:父类对象引用要变成子类对象引用?对象的浅复制?怎么看都不像是向上转型。。
        aFather2.f(); 
        aSon2.f();
        System.out.println(aFather2.a); //疑惑2:既然方法f()用了子类的方法,为什么数据a没有变成子类的呢?
      }
    }
    运行结果是:
    1 
    in b
    in b
    1
    先把例子记录下来,等高手帮忙解答。
  • 2007-01-08 | java的final关键字【转】

    caicai1724 发布于 2007-04-27 18:07:49

    final关键字
    由于语境(应用环境)不同,final关键字的含义可能会稍微产生一些差异。但它最一般的意思就是声明“这个东西不能改变”。之所以要禁止改变,可能是考虑到两方面的因素:设计或效率。由于这两个原因颇有些区别,所以也许会造成final关键字的误用。
    在接下去的小节里,我们将讨论final关键字的三种应用场合:数据、方法以及类。

    一 final数据
    许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:
    (1) 编译期常数,它永远不会改变
    (2) 在运行期初始化的一个值,我们不希望它发生变化
    对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程里。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在Java中,这些形式的常数必须属于基本数据类型(Primitives),而且要用final关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值。
    无论static还是final字段,都只能存储一个数据,而且不得改变。
    若随同对象句柄使用final,而不是基本数据类型,它的含义就稍微让人有点儿迷糊了。对于基本数据类型,final会将值变成一个常数;但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有“常数”效果)。这一限制也适用于数组,它也属于对象。
    下面是演示final字段用法的一个例子:

    //: FinalData.java
    // The effect of final on fields

    class Value {
      int i = 1;
    }

    public class FinalData {
      // Can be compile-time constants
      final int i1 = 9;
      static final int I2 = 99;
      // Typical public constant:
      public static final int I3 = 39;
      // Cannot be compile-time constants:
      final int i4 = (int)(Math.random()*20);
      static final int i5 = (int)(Math.random()*20);
     
      Value v1 = new Value();
      final Value v2 = new Value();
      static final Value v3 = new Value();
      //! final Value v4; // Pre-Java 1.1 Error:
                          // no initializer
      // Arrays:
      final int[] a = { 1, 2, 3, 4, 5, 6 };

      public void print(String id) {
        System.out.println(
          id + ": " + "i4 = " + i4 +
          ", i5 = " + i5);
      }
      public static void main(String[] args) {
        FinalData fd1 = new FinalData();
        //! fd1.i1++; // Error: can't change value
        fd1.v2.i++; // Object isn't constant!
        fd1.v1 = new Value(); // OK -- not final
        for(int i = 0; i < fd1.a.length; i++)
          fd1.a[i]++; // Object isn't constant!
        //! fd1.v2 = new Value(); // Error: Can't
        //! fd1.v3 = new Value(); // change handle
        //! fd1.a = new int[3];

        fd1.print("fd1");
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData();
        fd1.print("fd1");
        fd2.print("fd2");
      }
    } ///:~

    由于i1和I2都是具有final属性的基本数据类型,并含有编译期的值,所以它们除了能作为编译期的常数使用外,在任何导入方式中也不会出现任何不同。I3是我们体验此类常数定义时更典型的一种方式:public表示它们可在包外使用;Static强调它们只有一个;而final表明它是一个常数。注意对于含有固定初始化值(即编译期常数)的fianl static基本数据类型,它们的名字根据规则要全部采用大写。也要注意i5在编译期间是未知的,所以它没有大写。
    不能由于某样东西的属性是final,就认定它的值能在编译时期知道。i4和i5向大家证明了这一点。它们在运行期间使用随机生成的数字。例子的这一部分也向大家揭示出将final值设为static和非static之间的差异。只有当值在运行期间初始化的前提下,这种差异才会揭示出来。因为编译期间的值被编译器认为是相同的。这种差异可从输出结果中看出:

    fd1: i4 = 15, i5 = 9
    Creating new FinalData
    fd1: i4 = 15, i5 = 9
    fd2: i4 = 10, i5 = 9

    注意对于fd1和fd2来说,i4的值是唯一的,但i5的值不会由于创建了另一个FinalData对象而发生改变。那是因为它的属性是static,而且在载入时初始化,而非每创建一个对象时初始化。
    从v1到v4的变量向我们揭示出final句柄的含义。正如大家在main()中看到的那样,并不能认为由于v2属于final,所以就不能再改变它的值。然而,我们确实不能再将v2绑定到一个新对象,因为它的属性是final。这便是final对于一个句柄的确切含义。我们会发现同样的含义亦适用于数组,后者只不过是另一种类型的句柄而已。将句柄变成final看起来似乎不如将基本数据类型变成final那么有用。

    2. 空白final
    Java 1.1允许我们创建“空白final”,它们属于一些特殊的字段。尽管被声明成final,但却未得到一个初始值。无论在哪种情况下,空白final都必须在实际使用前得到正确的初始化。而且编译器会主动保证这一规定得以贯彻。然而,对于final关键字的各种应用,空白final具有最大的灵活性。举个例子来说,位于类内部的一个final字段现在对每个对象都可以有所不同,同时依然保持其“不变”的本质。下面列出一个例子:

    //: BlankFinal.java
    // "Blank" final data members

    class Poppet { }

    class BlankFinal {
      final int i = 0; // Initialized final
      final int j; // Blank final
      final Poppet p; // Blank final handle
      // Blank finals MUST be initialized
      // in the constructor:
      BlankFinal() {
        j = 1; // Initialize blank final
        p = new Poppet();
      }
      BlankFinal(int x) {
        j = x; // Initialize blank final
        p = new Poppet();
      }
      public static void main(String[] args) {
        BlankFinal bf = new BlankFinal();
      }
    } ///:~

    现在强行要求我们对final进行赋值处理——要么在定义字段时使用一个表达 式,要么在每个构建器中。这样就可以确保final字段在使用前获得正确的初始化。

    3. final自变量
    Java 1.1允许我们将自变量设成final属性,方法是在自变量列表中对它们进行适当的声明。这意味着在一个方法的内部,我们不能改变自变量句柄指向的东西。如下所示:

    //: FinalArguments.java
    // Using "final" with method arguments

    class Gizmo {
      public void spin() {}
    }

    public class FinalArguments {
      void with(final Gizmo g) {
        //! g = new Gizmo(); // Illegal -- g is final
        g.spin();
      }
      void without(Gizmo g) {
        g = new Gizmo(); // OK -- g not final
        g.spin();
      }
      // void f(final int i) { i++; } // Can't change
      // You can only read from a final primitive:
      int g(final int i) { return i + 1; }
      public static void main(String[] args) {
        FinalArguments bf = new FinalArguments();
        bf.without(null);
        bf.with(null);
      }
    } ///:~

    注意此时仍然能为final自变量分配一个null(空)句柄,同时编译器不会捕获它。这与我们对非final自变量采取的操作是一样的。
    方法f()和g()向我们展示出基本类型的自变量为final时会发生什么情况:我们只能读取自变量,不可改变它。

    二 final方法
    之所以要使用final方法,可能是出于对两方面理由的考虑。第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
    采用final方法的第二个理由是程序执行的效率。将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提升都被花在方法内部的时间抵消了。Java编译器能自动侦测这些情况,并颇为“明智”地决定是否嵌入一个final方法。然而,最好还是不要完全相信编译器能正确地作出所有判断。通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。
    类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。

    三 final类
    如果说整个类都是final(在它的定义前冠以final关键字),就表明自己不希望从这个类继承,或者不允许其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。
    除此以外,我们或许还考虑到执行效率的问题,并想确保涉及这个类各对象的所有行动都要尽可能地有效。如下所示:

    //: Jurassic.java
    // Making an entire class final

    class SmallBrain {}

    final class Dinosaur {
      int i = 7;
      int j = 1;
      SmallBrain x = new SmallBrain();
      void f() {}
    }

    //! class Further extends Dinosaur {}
    // error: Cannot extend final class 'Dinosaur'

    public class Jurassic {
      public static void main(String[] args) {
        Dinosaur n = new Dinosaur();
        n.f();
        n.i = 40;
        n.j++;
      }
    } ///:~

    注意数据成员既可以是final,也可以不是,取决于我们具体选择。应用于final的规则同样适用于数据成员,无论类是否被定义成final。将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final一样,编译器此时有相同的效率选择。
    可为final类内的一个方法添加final指示符,但这样做没有任何意义。

    四 final的注意事项
    设计一个类时,往往需要考虑是否将一个方法设为final。可能会觉得使用自己的类时执行效率非常重要,没有人想覆盖自己的方法。这种想法在某些时候是正确的。
    但要慎重作出自己的假定。通常,我们很难预测一个类以后会以什么样的形式再生或重复利用。常规用途的类尤其如此。若将一个方法定义成final,就可能杜绝了在其他程序员的项目中对自己的类进行继承的途径,因为我们根本没有想到它会象那样使用。
    标准Java库是阐述这一观点的最好例子。其中特别常用的一个类是Vector。如果我们考虑代码的执行效率,就会发现只有不把任何方法设为final,才能使其发挥更大的作用。我们很容易就会想到自己应继承和覆盖如此有用的一个类,但它的设计者却否定了我们的想法。但我们至少可以用两个理由来反驳他们。首先,Stack(堆栈)是从Vector继承来的,亦即Stack“是”一个Vector,这种说法是不确切的。其次,对于Vector许多重要的方法,如addElement()以及elementAt()等,它们都变成了synchronized(同步的)。正如在第14章要讲到的那样,这会造成显著的性能开销,可能会把final提供的性能改善抵销得一干二净。因此,程序员不得不猜测到底应该在哪里进行优化。在标准库里居然采用了如此笨拙的设计,真不敢想象会在程序员里引发什么样的情绪。
    另一个值得注意的是Hashtable(散列表),它是另一个重要的标准类。该类没有采用任何final方法。正如我们在本书其他地方提到的那样,显然一些类的设计人员与其他设计人员有着全然不同的素质(注意比较Hashtable极短的方法名与Vecor的方法名)。对类库的用户来说,这显然是不应该如此轻易就能看出的。一个产品的设计变得不一致后,会加大用户的工作量。这也从另一个侧面强调了代码设计与检查时需要很强的责任心。

  • 2007-01-09 | java运行时多态性的实现[转]

    caicai1724 发布于 2007-04-27 18:09:01

    java运行时多态性的实现

    运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制,下面就继承和接口实现两方面谈谈java运行时多态性的实现。

    一、通过继承中超类对象引用变量引用子类对象来实现

    举例说明:
    //定义超类superA
    class superA
    {
    int i = 100;
    void fun()
    {
    System.out.println(“This is superA”);
    }
    }
    //定义superA的子类subB
    class subB extends superA
    {
    int m = 1;
    void fun()
    {
    System.out.println(“This is subB”);
    }
    }
    //定义superA的子类subC
    class subC extends superA
    {
    int n = 1;
    void fun()
    {
    System.out.println(“This is subC”);
    }
    }

    class Test
    {
    public static void main(String[] args)
    {
    superA a;
    subB b = new subB();
    subC c = new subC();
    a=b;
    a.fun(); (1)
    a=c;
    a.fun(); (2)
    }
    }

    运行结果为:
    This is subB
    This is subC

    上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b, c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:“为什么(1)和(2)不输出:This is superA”。java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
    所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值,指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。

    另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。
    不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,否则子类必须被abstract修饰符修饰,当然也就不能被实例化了。

    二、通过接口类型变量引用实现接口的类的对象来实现

    接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似。

    举例说明:
    //定义接口InterA
    interface InterA
    {
    void fun();
    }
    //实现接口InterA的类B
    class B implements InterA
    {
    public void fun()
    {
    System.out.println(“This is B”);
    }
    }

    //实现接口InterA的类C
    class C implements InterA
    {
    public void fun()
    {
    System.out.println(“This is C”);
    }
    }

    class Test
    {
    public static void main(String[] args)
    {
    InterA a;
    a= new B();
    a.fun();
    a = new C();
    a.fun();
    }
    }
    输出结果为:
    This is B
    This is C

    上例中类B和类C是实现接口InterA的两个类,分别实现了接口的方法fun(),通过将类B和类C的实例赋给接口引用a而实现了方法在运行时的动态绑定,充分利用了“一个接口,多个方法”展示了Java的动态多态性。

    需要注意的一点是:Java在利用接口变量调用其实现类的对象的方法时,该方法必须已经在接口中被声明,而且在接口的实现类中该实现方法的类型和参数必须与接口中所定义的精确匹配。

    结束语:以上就是java运行时多态性的实现方法,大家在编程过程中可以灵活运用,但是在性能要求较高的代码中不提倡运用运行时多态,毕竟Java的运行时动态方法调用较之普通的方法调用的系统开销是比较大的。
  • 无模式对话框

    wxq8102 发布于 2007-04-27 18:10:15

        今天学了一个新概念,无模式对话框:例如,单击窗口中某链接弹出一个小的对话框,然后可以在父窗口继续对地图进行操作,而上面的子窗口不会消失.

    一般在网页中实现是采用javascrīpt,而在c/s则采用面向对象的语言开发.

        下面是vc++代码的实现:

    创建无模式对话框需要调用BOOL CDialog::Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );之后还需要调用BOOL CDialog::ShowWindow( SW_SHOW);进行显示,否则无模式对话框将是不可见的。相关代码如下:  

    void CYourView::OnOpenDlg(void)

    {

           /*假设IDD_TEST_DLG为已经定义的对话框资源的ID*/

           CTestDlg *dlg=new CTestDlg;

           dlg->Create(IDD_TEST_DLG,NULL);

           dlg->ShowWindows(SW_SHOW);

           /*不要调用 delete dlg;*/

    }

        在上面的代码中我们新生成了一个对话框对象,而且在退出函数时并没有销毁该对象。因为如果此时销毁该对象(对象被销毁时窗口同时被销毁),而此时对话框还在显示就会出现错误。那么这就提出了一个问题:什么时候销毁该对象。我时常使用的方法有两个:

    一、在对话框退出时销毁自己:在对话框中重载OnOKOnCancel在函数中调用父类的同名函数,然后调用DestroyWindow()强制销毁窗口,在对话框中映射WM_DESTROY消息,在消息处理函数中调用delete this;强行删除自身对象。相关代码如下:

    void CTestDlg1::OnOK()

    {

           CDialog::OnOK();

           DestroyWindow();

    }

     

    void CTestDlg1::OnCancel()

    {

           CDialog::OnCancel();

           DestroyWindow();

    }

     

    void CTestDlg1::OnDestroy()

    {

           CDialog::OnDestroy();

           AfxMessageBox("call delete this");

           delete this;

    }

     

    这种方法的要点是在窗口被销毁的时候,删除自身对象。所以你可以在任何时候调用DestroyWindow()以达到彻底销毁自身对象的作用。(DestroyWindow()的调用会引起OnDestroy()的调用)

     

    二、通过向父亲窗口发送消息,要求其他窗口对其进行销毁:首先需要定义一个消息用于进行通知,然后在对话框中映射WM_DESTROY消息,在消息处理函数中调用消息发送函数通知其他窗口。在接收消息的窗口中利用ON_MESSAGE映射处理消息的函数,并在消息处理函数中删除对话框对象。相关代码如下:

    /*更改对话框的有关文件*/

    CTestDlg2::CTestDlg2(CWnd* pParent /*=NULL*/)

           : CDialog(CTestDlg2::IDD, pParent)

    {/*m_pParent为一成员变量,用于保存通知窗口的指针,所以该指针不能是一个临时指针*/

           ASSERT(pParent);

           m_pParent=pParent;

           //{{AFX_DATA_INIT(CTestDlg2)

                  // NOTE: the ClassWizard will add member initialization here

           //}}AFX_DATA_INIT

    }

    void CTestDlg2::OnOK()

    {

           CDialog::OnOK();

           DestroyWindow();

    }

     

    void CTestDlg2::OnCancel()

    {

           CDialog::OnCancel();

           DestroyWindow();

    }

     

    void CTestDlg2::OnDestroy()

    {

           CDialog::OnDestroy();

           /*向其他窗口发送消息,将自身指针作为一个参数发送*/

           m_pParent->PostMessage(WM_DELETE_DLG,(WPARAM)this);

    }

     

    /*在消息接收窗口中添加消息映射*/

    /*在头文件中添加函数定义*/

           afx_msg LONG OnDelDlgMsg(WPARAM wP,LPARAM lP);

    /*添加消息映射代码*/

           ON_MESSAGE(WM_DELETE_DLG,OnDelDlgMsg)

    END_MESSAGE_MAP()

    /*实现消息处理函数*/

    LONG CMy53_s1View::OnDelDlgMsg(WPARAM wP,LPARAM lP)

    {

           delete (CTestDlg2*)wP;

           return 0;

    }

    /*创建对话框*/

    void CMy53_s1View::OnTest2()

    {

           CTestDlg2 *dlg=new CTestDlg2(this);

           dlg->Create(IDD_TEST_DLG_2);

           dlg->ShowWindow(SW_SHOW);

    }

     

    在这种方法中我们利用消息来进行通知,在Window系统中利用消息进行通知和传递数据的用法是很多的。

    同样无模式对话框的另一个作用还可以用来在用户在对话框中的输入改变时可以及时的反映到其他窗口。下面的代码演示了在对话框中输入一段文字,然后将其更新到视图的显示区域中,这同样也是利用了消息进行通知和数据传递。

     

    /*在对话框中取出数据,并向其他窗口发送消息和数据,将数据指针作为一个参数发送*/

    void CTestDlg2::OnCommBtn()

    {

           char szOut[30];

           GetDlgItemText(IDC_OUT,szOut,30);

           m_pParent->SendMessage(WM_DLG_NOTIFY,(WPARAM)szOut);

    }

     

    /*在消息接收窗口中*/

    /*映射消息处理函数*/

           ON_MESSAGE(WM_DLG_NOTIFY,OnDlgNotifyMsg)

     

    /*在视图中绘制出字符串 m_szOut*/

    void CMy53_s1View::OnDraw(CDC* pDC)

    {

           CMy53_s1Doc* pDoc = GetDocument();

           ASSERT_VALID(pDoc);

           // TODO: add draw code for native data here

           pDC->TextOut(0,0,"Display String");

           pDC->TextOut(0,20,m_szOut);

    }

    /*处理通知消息,保存信息并更新显示*/

    LONG CMy53_s1View::OnDlgNotifyMsg(WPARAM wP,LPARAM lP)

    {

           m_szOut=(char*)wP;

           Invalidate();

           return 0;

    }

     

  • 2007-01-05 | 进程中出现多个IEXPLORE.EXE解决之道!

    caicai1724 发布于 2007-04-27 18:02:04

    哈哈,今天上午电脑出现了点小意外,任务管理器中又出现了多个IEXPLORE.EXE进程,以前也遇到过,但没有解决它就重装系统了,我用百度搜了搜,被我成功找到了解决的方法,太感谢下面帖中的16楼和26楼了。

    http://post.baidu.com/f?kz=140571260

    顺便在我的博客中记录下来,方便博客朋友查看

    1、点击:“开始”、“运行”。键入regedit,按回车。清理注册表:

    (1)展开:HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows

    删除:"load"=""

     (2)展开:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\Explorer\Run

    删除:"twin"="X:\\windows\\system32\\twunk32.exe" 

    2、重启。显示隐藏文件。

    3、删除X:\windows\system32\twunk32.exe。

    4、卸载QQ。重新安装。因为QQ文件夹中的TIMPlatform.exe已被病毒覆盖。

    问题:

    1、无法显示隐藏文件?

    进行如下操作打开注册表 打开运行输入"regedit" 
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL下的CheckedValue删除,再新建一个DWORD值命名为CheckedValue,数值取1即可.这是病毒把CheckedValue这项改成字符串值而非原来的DWORD值了

    2、还是无法显示twunk32.exe和TIMPlatform.exe文件?

    实际上,我用瑞星杀毒软件扫描时清楚的看到有扫到那2个文件!可是就是显示不出来!还好还有一招

    用强制删除工具 PowerRMV 下载地址 http://post.baidu.com/f?kz=158203765
    分别填入下面的文件(包括完整的路径) ,勾选“抑止杀灭对象再次生成”,点杀灭
     C:\WINDOWS\system32\windhcp.ocx
    C:\WINDOWS\system32\twunk32.exe

    C:\Program Files\Tencent\qq\TIMPLATFROM.exe

    原文地址为:http://hi.baidu.com/teyqiu/blog/item/a3254f4ad52ac72309f7eff3.html

  • 2007-01-16 | java的ArrayList使用【转】

    caicai1724 发布于 2007-04-27 22:04:33

    1、什么是ArrayList
        ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处:

    • 动态的增加和减少元素
    • 实现了ICollectionIList接口
    • 灵活的设置数组的大小


    2、如何使用ArrayList
        最简单的例子:

    ArrayList List = new ArrayList();
    for( int i=0;i<10;i++ ) //
    给数组增加10Int元素
    List.Add(i); 
    //..
    程序做一些处理
    List.RemoveAt(5);//
    将第6个元素移除
    for( int i=0;i<3;i++ ) //
    再增加3个元素
      List.Add(i+20);
    Int32[] values = (Int32[])List.ToArray(typeof(Int32));//
    返回ArrayList包含的数组

    这是一个简单的例子,虽然没有包含ArrayList所有的方法,但是可以反映出ArrayList最常用的用法

    3
    ArrayList重要的方法和属性
    1)构造器
        ArrayList
    提供了三个构造器:
    public ArrayList();
    默认的构造器,将会以默认(16)的大小来初始化内部的数组
    public ArrayList(ICollection);
    用一个ICollection对象来构造,并将该集合的元素添加到ArrayList
    public ArrayList(int);
    用指定的大小来初始化内部的数组


    2IsSynchronized属性和ArrayList.Synchronized方法
        IsSynchronized属性指示当前的ArrayList实例是否支持线程同步,而ArrayList.Synchronized静态方法则会返回一个ArrayList的线程同步的封装。
        如果使用非线程同步的实例,那么在多线程访问的时候,需要自己手动调用lock来保持线程同步,例如:
    ArrayList list = new ArrayList();
    //...
    lock( list.SyncRoot ) //ArrayList为非线程包装的时候,SyncRoot属性其实就是它自己,但是为了满足ICollectionSyncRoot定义,这里还是使用SyncRoot来保持源代码的规范性

    {
    list.Add( 
    Add a Item );
    }

         如果使用ArrayList.Synchronized方法返回的实例,那么就不用考虑线程同步的问题,这个实例本身就是线程安全的,实际上ArrayList内部实现了一个保证线程同步的内部类,ArrayList.Synchronized返回的就是这个类的实例,它里面的每个属性都是用了lock关键字来保证线程同步。

    ****

    但是,使用这个方法(ArrayList.Synchronized)并不能保证枚举的同步,例如,一个线程正在删除或添加集合项,而另一个线程同时进行枚举,这时枚举将会抛出异常。所以,在枚举的时候,你必须明确使用 SyncRoot 锁定这个集合。

     

    HashtableArrayList关于线程安全性的使用方法类似。

    ****

    3Count属性和Capacity属性
        Count
    属性是目前ArrayList包含的元素的数量,这个属性是只读的。
    Capacity
    属性是目前ArrayList能够包含的最大数量,可以手动的设置这个属性,但是当设置为小于Count值的时候会引发一个异常。

    4AddAddRangeRemoveRemoveAtRemoveRangeInsertInsertRange
        
    这几个方法比较类似

    Add
    方法用于添加一个元素到当前列表的末尾
    AddRange
    方法用于添加一批元素到当前列表的末尾
    Remove
    方法用于删除一个元素,通过元素本身的引用来删除
    RemoveAt
    方法用于删除一个元素,通过索引值来删除
    RemoveRange
    用于删除一批元素,通过指定开始的索引和删除的数量来删除
    Insert
    用于添加一个元素到指定位置,列表后面的元素依次往后移动
    InsertRange
    用于从指定位置开始添加一批元素,列表后面的元素依次往后移动

        
    另外,还有几个类似的方法:
    Clear
    方法用于清除现有所有的元素
    Contains
    方法用来查找某个对象在不在列表之中

        
    其他的我就不一一累赘了,大家可以查看MSDN,上面讲的更仔细
    5TrimSize方法
        
    这个方法用于将ArrayList固定到实际元素的大小,当动态数组元素确定不在添加的时候,可以调用这个方法来释放空余的内存。
    6ToArray方法
        
    这个方法把ArrayList的元素Copy到一个新的数组中。


    4
    ArrayList与数组转换
        1
    ArrayList List = new ArrayList();
    List.Add(1);
    List.Add(2);
    List.Add(3);

    Int32[] values = (Int32[])List.ToArray(typeof(Int32));

        
    2
    ArrayList List = new ArrayList();
    List.Add(1);
    List.Add(2);
    List.Add(3);

    Int32[] values = new Int32[List.Count];
    List.CopyTo(values);

        
    上面介绍了两种从ArrayList转换到数组的方法

        
    3
    ArrayList List = new ArrayList();
    List.Add( 
    string );
    List.Add( 1 );
    //
    往数组中添加不同类型的元素


    object[] values = List.ToArray(typeof(object)); //
    正确
    string[] values = (string[])List.ToArray(typeof(string)); //
    错误

    和数组不一样,因为可以转换为Object数组,所以往ArrayList里面添加不同类型的元素是不会出错的,但是当调用ArrayList方法的时候,要么传递所有元素都可以正确转型的类型或者Object类型,否则将会抛出无法转型的异常。


    5
    ArrayList最佳使用建议
        这一节我们来讨论ArrayList与数组的差别,以及ArrayList的效率问题
      
    1ArrayListArray的复杂版本
    ArrayList
    内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如IndexIndexOfContainsSort等都是在内部数组的基础上直接调用Array的对应方法。
      
    2)内部的Object类型的影响
             
    对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。
    但是恰恰对于大多数人,多数的应用都是使用值类型的数组。
    消除这个影响是没有办法的,除非你不用它,否则就要承担一部分的效率损失,不过这部分的损失不会很大。
      
    3)数组扩容
    这是对ArrayList效率影响比较大的一个因素。
    每当执行AddAddRangeInsertInsertRange等添加元素的方法,都会检查内部数组的容量是否不够了,如果是,它就会以当前容量的两倍来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组,在这个临界点的扩容操作,应该来说是比较影响效率的。
         
    1:比如,一个可能有200个元素的数据动态添加到一个以默认16个元素大小创建的ArrayList中,将会经过:
    16*2*2*2*2 = 256
    四次的扩容才会满足最终的要求,那么如果一开始就以:
    ArrayList List = new ArrayList( 210 );
    的方式创建ArrayList,不仅会减少4次数组创建和Copy的操作,还会减少内存使用。

         
    2:预计有30个元素而创建了一个ArrayList
    ArrayList List = new ArrayList(30);
    在执行过程中,加入了31个元素,那么数组会扩充到60个元素的大小,而这时候不会有新的元素再增加进来,而且有没有调用TrimSize方法,那么就有1次扩容的操作,并且浪费了29个元素大小的空间。如果这时候,用:
    ArrayList List = new ArrayList(40);
    那么一切都解决了。
    所以说,正确的预估可能的元素,并且在适当的时候调用TrimSize方法是提高ArrayList使用效率的重要途径。
       
    4)频繁的调用IndexOfContains等方法(SortBinarySearch等方法经过优化,不在此列)引起的效率损失
    首先,我们要明确一点,ArrayList是动态数组,它不包括通过Key或者Value快速访问的算法,所以实际上调用IndexOfContains等方法是执行的简单的循环来查找元素,所以频繁的调用此类方法并不比你自己写循环并且稍作优化来的快,如果有这方面的要求,建议使用HashtableSortedList等键值对的集合。
    ArrayList al=new ArrayList();

    al.Add("How");
    al.Add("are");
    al.Add("you!");

    al.Add(100);
    al.Add(200);
    al.Add(300);

    al.Add(1.2);
    al.Add(22.8);

    .........

    //
    第一种遍历 ArrayList 对象的方法
    foreach(object o in al)
    {
    Console.Write(o.ToString()+" ");
    }

    //
    第二种遍历 ArrayList 对象的方法
    IEnumerator ie=al.GetEnumerator();
    while(ie.MoveNext())
    {
    Console.Write(ie.Curret.ToString()+" ");
    }

    //
    第三种遍历 ArrayList 对象的方法
    我忘记了,好象是 利用 ArrayList对象的一个属性,它返回一此对象中的元素个数.

    然后在利用索引
     
    for(int i=0;i<Count;i++)
    {
    Console.Write(al[i].ToString()+" ");
    }

  • 2007-01-16 | java的ArrayList源码解读【转】

    caicai1724 发布于 2007-04-27 22:03:38

    Java 2源码解读:java.util.ArrayList


    ArrayList是List接口的一个可变长数组实现。实现了所有List接口的操作,并允许存储null值。除了没有进行同步,ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步,但ArrayList仅对writeObject和readObject进行了同步,其它比如add(Object)、remove(int)等都没有同步。

    1.存储

    ArrayList使用一个Object的数组存储元素。
    private transient Object elementData[];
    ArrayList实现了java.io.Serializable接口,这儿的transient标示这个属性不需要自动序列化。下面会在writeObject()方法中详细讲解为什么要这样作。

    2.add和remove


        public boolean add(Object o) {
        ensureCapacity(size + 1);  // Increments modCount!!
        elementData[size++] = o;
        return true;
        }

    注意这儿的ensureCapacity()方法,它的作用是保证elementData数组的长度可以容纳一个新元素。在“自动变长机制”中将详细讲解。
        public Object remove(int index) {
        RangeCheck(index);
        modCount++;
        Object oldValue = elementData[index];
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                     numMoved);
        elementData[--size] = null; // Let gc do its work
        return oldValue;
        }

    RangeCheck()的作用是进行边界检查。由于ArrayList采用一个对象数组存储元素,所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null,具体的对象的销毁由垃圾收集器负责。
    modCount的作用将在下面的“iterator()中的同步”中说明。
    注:在前移时使用了System提供的一个实用方法:arraycopy(),在本例中可以看出System.arraycopy()方法可以对同一个数组进行操作,这个方法是一个native方法,如果对同一个数组进行操作时,会首先把从源部分拷贝到一个临时数组,在把临时数组的元素拷贝到目标位置。

    3.自动变长机制

    在实例化一个ArrayList时,你可以指定一个初始容量。这个容量就是elementData数组的初始长度。如果你使用:
        ArrayList list = new ArrayList();

    则使用缺省的容量:10。
        public ArrayList() {
        this(10);
        }

    ArrayList提供了四种add()方法,
    • public boolean add(Object o)
    • public void add(int index, Object element)
    • public boolean addAll(Collection c)
    • public boolean addAll(int index, Collection c)

    在每一种add()方法中,都首先调用了一个ensureCapacity(int miniCapacity)方法,这个方法保证elementData数组的长度不小于miniCapacity。ArrayList的自动变长机制就是在这个方法中实现的。
        public void ensureCapacity(int minCapacity) {
        modCount++;
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
                if (newCapacity < minCapacity)
            newCapacity = minCapacity;
            elementData = new Object[newCapacity];
            System.arraycopy(oldData, 0, elementData, 0, size);
        }
        }

    从这个方法实现中可以看出ArrayList每次扩容,都扩大到原来大小的1.5倍。
    每种add()方法的实现都大同小异,下面给出add(Object)方法的实现:
        public boolean add(Object o) {
        ensureCapacity(size + 1);  // Increments modCount!!
        elementData[size++] = o;
        return true;
        }

    4.iterator()中的同步

    在父类AbstractList中定义了一个int型的属性:modCount,记录了ArrayList结构性变化的次数。
        protected transient int modCount = 0;

    在ArrayList的所有涉及结构变化的方法中都增加modCount的值,包括:add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次,modCount的值就加1。
    注:add()及addAll()方法的modCount的值是在其中调用的ensureCapacity()方法中增加的。

    AbstractList中的iterator()方法(ArrayList直接继承了这个方法)使用了一个私有内部成员类Itr,生成一个Itr对象(Iterator接口)返回:
        public Iterator iterator() {
        return new Itr();
        }

    Itr实现了Iterator()接口,其中也定义了一个int型的属性:expectedModCount,这个属性在Itr类初始化时被赋予ArrayList对象的modCount属性的值。
        int expectedModCount = modCount;

    注:内部成员类Itr也是ArrayList类的一个成员,它可以访问所有的AbstractList的属性和方法。理解了这一点,Itr类的实现就容易理解了。

    在Itr.hasNext()方法中:
        public boolean hasNext() {
            return cursor != size();
        }

    调用了AbstractList的size()方法,比较当前光标位置是否越界。

    在Itr.next()方法中,Itr也调用了定义在AbstractList中的get(int)方法,返回当前光标处的元素:
        public Object next() {
            try {
            Object next = get(cursor);
            checkForComodification();
            lastRet = cursor++;
            return next;
            } catch(IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
            }
        }

    注意,在next()方法中调用了checkForComodification()方法,进行对修改的同步检查:
        final void checkForComodification() {
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        }

    现在对modCount和expectedModCount的作用应该非常清楚了。在对一个集合对象进行跌代操作的同时,并不限制对集合对象的元素进行操作,这些操作包括一些可能引起跌代错误的add()或remove()等危险操作。在AbstractList中,使用了一个简单的机制来规避这些风险。这就是modCount和expectedModCount的作用所在。

    5.序列化支持

    ArrayList实现了java.io.Serializable接口,所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下:
    • private static final long serialVersionUID = 8683452581122892189L;
    • private transient Object elementData[];
    • private int size;

    可以看出serialVersionUID和size都将自动序列化到介质中,但elementData数组对象却定义为transient了。也就是说ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现?因为elementData数组中存储的“元素”其实仅是对这些元素的一个引用,并不是真正的对象,序列化一个对象的引用是毫无意义的,因为序列化是为了反序列化,当你反序列化时,这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进行序列化操作。这就是writeObject()的作用。
        private synchronized void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException{
        // Write out element count, and any hidden stuff
        s.defaultWriteObject();
       // Write out array length
        s.writeInt(elementData.length);
        // Write out all elements in the proper order.
        for (int i=0; i<size; i++)
                s.writeObject(elementData[i]);
        }

    这样元素数组elementData中的所以元素对象就可以正确地序列化到存储介质了。
    对应的readObject()也按照writeObject()方法的顺序从输入流中读取:
        private synchronized void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();
        // Read in array length and allocate array
        int arrayLength = s.readInt();
        elementData = new Object[arrayLength];
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++)
                elementData[i] = s.readObject();
        }
  • 2007-01-15 | java的异常处理(3)【转】

    caicai1724 发布于 2007-04-27 22:02:38

    Java中异常机制的深入研究

    由于本文旨在探讨Java"异常机制"的深层原理,因此关于"异常"的使用方法都不做详细说明。首先看一段非常熟悉的用于打开一个文件的C程序段:

    FILE *fp;
    fp=fopen(filename,"rw");
    if(fp==NULL){
        printf("cannot open file\n");
        exit(0);
    }


    在这段程序中,if条件语句中的一段用来处理没有找到指定文件,或者其它原因无法正确打开指定文件。可是如果遇到一个责任心不强的程序员,他可能认为出现找不到文件的可能性很小,或者由于思路集中在程序功能的实现上而忘记了处理这种情况。这时程序同样可以正确编译,而且一般情况下也不会出现问题。但此时这段程序可以肯定说是不够健壮的,而且一旦这段程序发生了错误也会让程序员很难发现错误出在哪里。在C语言以及其它大多数高级语言中都可以举出很多这种例子。

    也就是一个函数在使用的时候,可能会出现并没有达到这个函数的使用目的的情况,哪怕在这段程序的特定使用环境下发生这种异常情况的可能性只有万分之一。常用处理的方法就是,程序员在需要使用某个函数时必须充分了解可能会有什么原因导致该函数不能正确执行,然后加入相应的条件判断语句来进行处理。后面将有一个例子说明这个问题。

    而Java的"异常机制"就是在处理上述问题中给了程序员非常简单而灵活的方式。一般来说,其它高级语言主要是让函数使用者来关注该函数可能会出现的异常情况,而java则是把这件事情交给方法(和函数对应的概念,在Java中称方法)的设计者来做。这对于方法的使用者来说带来的方便是不会因为责任心不强,或者办事丢三那四,会忘了在使用方法时处理可能发生的异常情况。而麻烦就是,在使用一个可能会发生异常的方法时,绝对不能视而不见,而必须做出相应的处理。也就是说象上述C程序段中,如果忘了if程序块,这个程序甚至还能蒙过一个外行上司,但当使用Java来完成这个功能时,只要用到的方法使用了"异常"机制,如果不对可能产生"异常"的方法进行相应处理,java编译器是不会让其通过的。

    一、"异常类"的组织形式

    Java系统类中的方法产生的异常都被组织成"异常类"(还有Error类,不在本文讨论范围),此方法和它相关的"异常类"通过throws关键字关联在一起,并且这些类都必须是Exception类的子类。任何一个自己开发的类的方法中如果可能会产生某种异常,也可以将这种异常组织成一个"异常类",但这个"异常类"同样必须是Exception的子类,或孙子类等等。

    例1:

    /*isLegal于检查数据是否合法,当>0时视为合法,返回合法值,
    *否则视为不合法,抛出"异常"。*/
    int isLegal(int dt) throws LowZeroException//这种定义本文中均称为方法与"异常"通
    {                  //过throws建立了关联
    	if(dt>=0){
    		return data;
    	}
    	else
    		throw new LowZeroException();
    }
    /*自已写的异常类,继承自Exception*/
    class LowZeroException extends Exception
    {
    	public LowZeroException(){
    		super();
    	}
    }


    仔细观察方法isLegal(),它体现出的最值得注意的特色是,它有两种方式的函数出口,一种是通过return语句,返回的是方法本身定义的类型的实例,另一种是通过throw,返回的是"异常类"的对象实例,Java中称之为抛出"异常"。对比一下C中如何处理同样的问题的:

    int isLegal(int dt) {
    	if(dt>=0){
    		return data;
    	}
    	else
    		return -1;//通过一个特定值来表明出错
    }


    由于C只能通过return返回函数值,所以在处理异常情况时则可能通过以上方式来处理。当然这就要求isLegal()函数的使用者必须知道函数中使用返回值-1来表明出现不合法数据的情况。

    对比这两种处理方法,可以知道java的"异常机制"把处理异常事件的职能和方法本身的职能通过两个不同出口分离开来。

    所有这些"异常类"独立于它具体服务的方法被统一组织成一个类树。"异常机制"就好比高校的后勤社会化一样,通过后勤社会化将学校的教学职能和学校的后勤保障分离开来,并且后勤集团的组织形式也是独立于学校主体的。事实证明,这种组织方式不仅提高了服务效率,也提高了服务质量。整个Java体系中的"异常类"组织形式如图1所示:





    在例1中的isLegal()方法如果在调用过程中没有能正常返回整形数,而是在"异常"产生点产生了"异常"对象,那么这个"异常"对象由谁来接收,并处理它呢?以下就来解答这个问题。

    二、"异常"的处理过程

    Java中由try…catch语法来处理"异常",将关联有"异常类"的方法包含在try{}程序块中,catch(){}关键字可以使用形参,用于和方法产生的"异常"对象结合。当调用某个方法时,引起异常事件发生的条件成立,便会抛出"异常",原来的程序流程将会在此方法处中断,然后try模块后紧跟的catch中的"形参"和此异常对象完成了结合,继而进入了catch模块中运行。具体过程举例说明:

    例2:

    /*将关联有异常的方法包含在try模块中*/
    int myMethod(int dt){
    int data = 0;
    try{
    	int data = isLegal(dt);
    }catch(LowZeroException e){
    	System.out.println("发生数据错误!");
    }
    		return data;
    	}


    三、"异常"的处理方法

    有两种方法处理"异常":第一种如例2,将含有"异常"出口的方法直接放到try块中,然后由紧随其后的catch块捕捉。第二种是不直接监听捕捉被引用方法的"异常",而是将这个"异常"关联传递给引用方法,同时监听捕捉工作也相应向上传递。

    例3:

    int myMethod2(int dt)
    {
    	int data = 0;
    try{
    		data = myMethod(dt)
    	}catch(LowZeroException e){
    		System.out.println("发生数据错误!");
    		e.printStackTrace();
    	}
    	return data;
    }
    
    int myMethod(int dt) throws LowZeroException
    {
    	int data = isLegal(dt); //此处引用isLegal()方法,但并没有捕捉它的"异常"
    return data; 
    }


    从上例中可以看到方法myMethod()与它引用的方法isLegal()产生的"异常"LowZeroException建立了关联,也就是完成了将"异常"关联的向上传递,此时的myMethod()方法体中虽然只有一个return返回语句,但它事实上同样有两种方式的函数出口,一种是由return返回的整形值,另一种则是返回方法名中的throws关键字所指的"异常类"的实例对象。相应的,监听捕捉的工作交给了上一层方法myMethod2()。同样的道理,myMethod2()也可以将"异常"通过throws的关联继续向上传递。这样的话,一旦一个"异常"被捕捉到时,这个"异常"必有一个传递路径,而如果我们在捕捉点的catch程序块中加入printStackTrace()方法,便能清楚的看到这个"异常"是怎样传递过来的。例如在例3如果有"异常"被捕捉到,e.printStackTrace()打印出来的结果将是:

    LowZeroException:

    at Example.isLegal

    at Example myMethod

    at Example.myMethod2

    at Example main

    从上结果中我们可以看到,从LowZeroException"异常"产生点,即包含throw new LowZeroException();子句的方法开始,然后一直追溯到产生当前线程的方法(注意:printStackTrace()并不是追溯到捕捉点结束,而是到产生当前线程的方法结束)。"异常"产生点产生的LowZeroException"异常"对象,首先被赋给了isLegal()关联的LowZeroException类的无名引用,然后继续赋给myMethod()关联的LowZeroException类的无名引用,再继续赋给myMethod2()中的catch块中的形参e,最后在这里被处理掉,这个"异常"对象随即消失。可以说,catch(){}就是"异常"对象的生命终结点。

    另外还要注意一点,方法与"异常"的关联可以一直向上传递,当传递到与main方法关联后,即在main()方法的定义中使用了throws Exception,这时除了虚拟机没有其它方法能够引用main()方法,且在程序中可能看不到try…catch程序块,但并不会产生错误,因为此时虚拟机会捕捉"异常",并且会默认的调用printStackTrace()方法打印出"异常"路径。总之只要一个方法关联了"异常",可以将这个"异常"关联向上传递,但是最终必须使用catch来终止"异常",或者一直传递到main()方法交给Java虚拟机来结束"异常"对象的生命,否则是通不过编译的。

    四、使用"异常机制"的需要注意的几点

    1.一个方法中可能会产生多种不同的异常,你可以设置多个"异常"抛出点来解决这个问题。

    2."异常"对象从产生点产生后,到被捕捉后终止生命的全过程中,实际上是一个传值过程,所以你可以根据需要,来合理的控制检测到"异常"的粒度。例如在例3中,如果你并不需要知道具体产生的是LowZeroException"异常",那么你可以使用"异常"的公共父类Exception来结合"异常"对象,即catch(Exception e){…}。同样在"异常"与方法关联的传递过程中,也可以根据需要控制关联"异常"的粒度,即throws后面跟上异常对象的父类名。

    3."异常机制"中还有一种特殊情况――RuntimeException"异常类"(见图1),这个"异常类"和它的所有子类都有一个特性,就是"异常"对象一产生就被Java虚拟机直接处理掉,即在方法中出现throw 子句的地方便被虚拟机捕捉了。因此凡是抛出这种"运行时异常"的方法在被引用时,不需要有try…catch语句来处理"异常"。
  • 2007-01-15 | java的异常处理(2)【转】

    caicai1724 发布于 2007-04-27 22:01:42

    Java中常见的异常

    作为一名游戏开发者,程序员,很自然必须熟悉对程序的调试方法。而要调试程序,自然需要对程序中的常见的异常有一定的了解,这些日子很多朋友都提出了很多问题,都是关于游戏中的报错,因此在这里我将一些常见的程序中的异常列举出来给大家参考:

      1. java.lang.NullPointerException
      这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空
    指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等。对数组操作中出现空指针,很多情况下是一些刚开始学习编程的朋友常犯的错误,即把数组的初始化和数组元素的初始化混淆起来了。数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空的,所以还需要对每个元素都进行初始化(如果要调用的话)

      2. java.lang.ClassNotFoundException
      这个异常是很多原本在JB等开发环境中开发的程序员,把JB下的程序包放在WTk下编译经常出现的问题,异常的解释是"指定的类不存在",这里主要考虑一下类的名称和路径是否正确即可,如果是在JB下做的程序包,一般都是默认加上Package的,所以转到WTK下后要注意把Package的路径加上。

      3. java.lang.ArithmeticException
      这个异常的解释是"数学运算异常",比如程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不是有不妥了。

      4. java.lang.ArrayIndexOutOfBoundsException
      这个异常相信很多朋友也经常遇到过,异常的解释是"数组下标越界",现在程序中大多都有对数组的操作,因此在调用数组的时候一定要认真检查,看自己调用的下标是不是超出了数组的范围,一般来说,显示(即直接用常数当下标)调用不太容易出这样的错,但隐式(即用变量表示下标)调用就经常出错了,还有一种情况,是程序中定义的数组的长度是通过某些特定方法决定的,不是事先声明的,这个时候,最好先查看一下数组的length,以免出现这个异常。

      5. java.lang.IllegalArgumentException
      这个异常的解释是"方法的参数错误",很多J2ME的类库中的方法在一些情况下都会引发这样的错误,比如音量调节方法中的音量参数如果写成负数就会出现这个异常,再比如g.setColor(int red,int green,int blue)这个方法中的三个值,如果有超过255的也会出现这个异常,因此一旦发现这个异常,我们要做的,就是赶紧去检查一下方法调用中的参数传递是不是出现了错误。

      6. java.lang.IllegalAccessException
      这个异常的解释是"没有访问权限",当应用程序要调用一个类,但当前的方法即没有对该类的访问权限便会出现这个异常。对程序中用了Package的情况下要注意这个异常。

      其他还有很多异常,我就不一一列举了,我要说明的是,一个合格的程序员,需要对程序中常见的问题有相当的了解和相应的解决办法,否则仅仅停留在写程序而不会改程序的话,会极大影响到自己的开发的。关于异常的全部说明,在API里都可以查阅。

我的栏目

数据统计

  • 访问量: 24181
  • 日志数: 35
  • 图片数: 3
  • 建立时间: 2007-05-08
  • 更新时间: 2008-05-26

RSS订阅

Open Toolbar