iptables学习心得

Iptables进程服务命令:

service iptables save 保存iptables设置,对iptables规则编辑后一定要保存。

service iptables restart 保存设置以后不重启则设置不生效,要设置生生效请重启。

service iptables status 检查iptables的设置。类似于iptable –L命令。

Iptables基本的链操作命令:

-L 列出某个链或者表中的规则: service iptables status 把这个命令和-L比较下

iptables –L:显示filter表中的规则等同于iptables –t filter -L

iptables –t nat –L :显示nat表的中的设置:

-F 删除某个链或者表中的规则:

iptables –F (iptables –t filter –F) 删除filter表中的所有规则;

iptables –t nat –F 删除nat表中的所有规则;

iptables –t nat –F POSTROUTING 删除nat表中POSTROUTING链的所有规则;

-A添加一条规则(在当前的规则后添加,也就是排在所有规则后):

iptables -A INPUT –s 192.168.0.1 –j DROP

和实例图中的功能相同,丢弃来自192.168.0.1的数据包,这里省略了-t filter。

添加该语句后,保存设置并重新启动iptalbes 服务,并通过-L的命令查看,就会发现刚添加的这条规则排列在所有规则后。

———–iptables的匹配规则是按顺序排列的。

-I在制定位置插入一条规则:

(如果有回环规则(iptables –A INPUT –I lo –j ACCEPT,则回环永远是第一条)

iptables –I 作为第一条规则插入。

iptables X 作为第X条规则插入,X这里代表规则顺序号。

iptables –A INPUT –p tcp –s 192.168.0.1 –dport 22 –j ACCEPT

允许192.168.0.1 通过22端口访问该主机,把它作为第一条规则插入iptables规则列表。

———–iptables的匹配规则是按顺序排列的。

-P 分配连接策略。

iptables –P INPUT DROP 禁止任何输入的数据包。这句慎用。

iptables –P OUTPUT ACCEPT 允许所有输出的数据包。

-D删除某一条规则:

Iptables –D X 删除某个链的第几条规则

iptables –D INPUT 3 删除INPUT链上的第3条规则。

iptables –P INPUT DROP 这个不能使用删除语句删除,只能到本机输入iptables –P INPUT ACCEPT

Iptables中的匹配:

iptables –A INPUT –p tcp –s 192.168.0.1 –dport 22 –j ACCEPT

这个命令我们在上面已经看过了,我们来看下其他的一些匹配参数。

-p protocol 匹配网络协议,例子中匹配tcp协议。

-s IP地址或者网段 匹配源IP地址或者网段

例子中师匹配一个IP的,如果要匹配一个网段则如下

-s 192.168.0.1/24

如果是除这个网段之外的所有则为:! -s 192.168.0.1/24

如果是除这个IP之外的所有则为:! -s 192.168.0.1

-d IP地址或者网段 匹配目的IP地址或者网段

–dport X 匹配目的端口号,X代表具体端口号。

–sport X 匹配源端口号,X代表具体端口号。

Iptables中的目的:

我们已经在前面看到过-j 后面跟的就是目的。

ACCEPT:允许数据包通过。

DROP:直接丢弃数据包。

REJECT:丢弃数据包,同时发送响应报文通知发送方。

设置Iptables预设规则(本地机防火墙):

1、清除iptables设置:iptables –F

2、设置回环规则,没有这个规则好多服务不能启动:

iptables –A INPUT –i lo –j ACCETP

3、连接跟踪设置:作用允许连线出去后对方主机回应进来的封包。

iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT

NEW:想要新建连接的数据包

INVALID:无效的数据包,例如损坏或者不完整的数据包

ESTABLISHED:已经建立连接的数据包

RELATED:与已经发送的数据包有关的数据包

4、iptables -p INPUT DROP 允许进入数据包—-慎用该句。

5、iptables -p FORWARD DROP 禁止转发数据包

6、iptables -P OUTPUT ACCEPT 允许外发数据包

7、设置好以后就可以根据情况开放相应的端口了

iptables –A INPUT –p tcp –dport 20:21 –j ACCEPT 开放FTP的20、21端口。

iptables –A INPUT –P tcp –dport 80 –j ACCEPT 开放http的80端口。

iptables –I INPUT –p tcp –dport 22 –j ACCEPT 开放SSH服务的22端口。

设置Iptables FORWORD规则:

一般情况FORWORD链式DROP的,但是当用来做NAT的时候我们就需要设置他了。

首先要开启转发功能:编辑/etc/sysctl.conf文件

Iptables转发功能(在做NAT时,FORWARD默认规则是DROP时,必须做)

# iptables -A FORWARD -i eth0 -o eth1 -m state –state RELATED,ESTABLISHED -j ACCEPT

# iptables -A FORWARD -i eth1 -o eh0 -j ACCEPT

丢弃坏的TCP包。

#iptables -A FORWARD -p TCP ! –syn -m state –state NEW -j DROP

处理IP碎片数量,防止攻击,允许每秒100个。

#iptables -A FORWARD -f -m limit –limit 100/s –limit-burst 100 -j ACCEPT

设置ICMP包过滤,允许每秒1个包,限制触发条件是10个包。

#iptables -A FORWARD -p icmp -m limit –limit 1/s –limit-burst 10 -j ACCEPT

我在前面只所以允许ICMP包通过,就是因为我在这里有限制。

连接跟踪:提供对数据包“状态”的检查

可以识别的状态:

NEW:想要新建连接的数据包

INVALID:无效的数据包,例如损坏或者不完整的数据包

ESTABLISHED:已经建立连接的数据包

RELATED:与已经发送的数据包有关的数据包

连接跟踪的模块

ip_conntrack_ftp:自动跟踪FTP连接,并自动打开它需要通过防火墙的高端端口。

Ip_conntrack_tftp:和上面功能类似不过是TFTP服务。

Ip_nat_ftp:修改NAT保护的计算机的FTP数据包。

Ip_nat_tftp:和上面类似不是TFTP数据包。

可以通过修改/etc/sysconfig/iptables-config 文件

修改IPTABLES_MODULES=”ip_conntrack_tftp ip_nat_ftp”

也可以通过modprobe ip_conntrack_tftp 不过重启以后将失效。

连接跟踪实例:

允许建立连接:

iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT

跟踪规则:

iptables -A INPUT -m state –state NEW -p tcp –dport 25 -j ACCEPT

阻止所有其他进入的链接:

iptables -A INPUT -m state –state NEW -j DROP

NAT网络地址转换:将一个IP转换成另一个 IP(输入和输出)

网络地址转换类型:

目的地 NAT(DNAT):DNAT修改包的目的地位址的时机,必须在包即将被送到本机行程之前,或是要被转送其它电脑之前;所以,使用DNAT为目标的规则,必须设置于nat表格的PREROUTING链结。

源 NAT(SNAT,MASQUERADE):SNAT必须在封包即将离开核心的前一刻,即时修改其来源位址(或通讯端口),所以SNAT规则的设置地点必须是在nat表格的POSTROUTING链结。

NAT实例:

iptables -t nat -A PREROUTING -i ethl -p tcp – -dport 80 -j DNAT – – to -destination 192.168.1.3:8080

把要进入eth1(eth1连接外网)80端口的数据包,重新定向到192.168.1.3的8080端口。

iptables -t nat -A PREROUTING -i eth1 -p tcp –dport 80 -j DNAT

–to-dest 192.168.1.3 –to-dest 192.168.1.4 –to-dest 192.168.1.5

上面这句第一个请求被发到192.168.1.3,第二个发到192.168.1.4如此循环,实现载量平衡。

上面的语句可以使外网的访问内网开通8080端口的WEB 服务器,那么内网的WEB服务器如何将数据传出去呢?这个时候要通过SNAT来实现了。

iptables -t nat -A POSTROUTING -j SNAT

iptables -t nat -A POSTROUTING -j SNAT –to-source 192.168.1.3-192.168.1.9

iptables -t nat -A POSTROUTING -j SNAT –to-source 192.168.1.3:123

iptables -t nat -A POSTROUTING -j SNAT –to-source 192.168.1.3:123-234

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

第一种方法是直接使用SNAT为目标,这种方法适合用在具有固定IP地址的网关器,另一种方法是使用是使用MASQUERADE为目标,适合用于只有动态IP地址的网关器(例如,使用PPPoE协定的ADSL连线)。由于由于MASQUERADE能够应付网络界面忽然离线,然后以另一个地址恢复上线的情况,所以它转换逻辑比较复杂些,需要耗损比较多的CPU运算能力,因此,如果你有固定的IP地址,就应该尽量使用SNAT来代替MASQUERADE。

ubuntu12.04教育网更新源

现在是已经发布到ubuntu 12.04,不过我用的还是11.10,电子科大的源今天挂了,写个文章存个档

更新源方法
1.备份:$sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
2.编辑:$sudo gedit /etc/apt/sources.list
删除文件里的所有,然后复制其中以下一个更新源到文件中,保存!
3.更新:$sudo apt-get update
4.升级:$sudo apt-get upgrade

ubuntu版本—版本号
Ubuntu 10.04 Lucid Lynx—lucid
Ubuntu 10.10 Maverick Meerkat—maverick
Ubuntu 11.04 Natty Narwhal—natty
Ubuntu 11.10 Oneiric Ocelot—oneiric
Ubuntu 12.04 Precise Pangolin—precise

教育网更新源:
ubuntu源的格式为 “deb url 版本号 main restricted universe multiverse”
一般来讲,url是不变的,所以只要修改版本号,就可以得到最新的源了

中国科技大学

deb http://debian.ustc.edu.cn/ubuntu/ precise main restricted universe multiverse
deb http://debian.ustc.edu.cn/ubuntu/ precise-backports restricted universe multiverse
deb http://debian.ustc.edu.cn/ubuntu/ precise-proposed main restricted universe multiverse
deb http://debian.ustc.edu.cn/ubuntu/ precise-security main restricted universe multiverse
deb http://debian.ustc.edu.cn/ubuntu/ precise-updates main restricted universe multiverse
deb-src http://debian.ustc.edu.cn/ubuntu/ precise main restricted universe multiverse
deb-src http://debian.ustc.edu.cn/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://debian.ustc.edu.cn/ubuntu/ precise-proposed main restricted universe multiverse
deb-src http://debian.ustc.edu.cn/ubuntu/ precise-security main restricted universe multiverse
deb-src http://debian.ustc.edu.cn/ubuntu/ precise-updates main restricted universe multiverse

上海交通大学

deb http://ftp.sjtu.edu.cn/ubuntu/ precise main multiverse restricted universe
deb http://ftp.sjtu.edu.cn/ubuntu/ precise-backports main multiverse restricted universe
deb http://ftp.sjtu.edu.cn/ubuntu/ precise-proposed main multiverse restricted universe
deb http://ftp.sjtu.edu.cn/ubuntu/ precise-security main multiverse restricted universe
deb http://ftp.sjtu.edu.cn/ubuntu/ precise-updates main multiverse restricted universe
deb http://ftp.sjtu.edu.cn/ubuntu-cn/ precise main multiverse restricted universe
deb-src http://ftp.sjtu.edu.cn/ubuntu/ precise main multiverse restricted universe
deb-src http://ftp.sjtu.edu.cn/ubuntu/ precise-backports main multiverse restricted universe
deb-src http://ftp.sjtu.edu.cn/ubuntu/ precise-proposed main multiverse restricted universe
deb-src http://ftp.sjtu.edu.cn/ubuntu/ precise-security main multiverse restricted universe
deb-src http://ftp.sjtu.edu.cn/ubuntu/ precise-updates main multiverse restricted universe

厦门大学的:

deb ftp://ubuntu.realss.cn/ubuntu/ precise main restricted universe multiverse
deb ftp://ubuntu.realss.cn/ubuntu/ precise-backports restricted universe multiverse
deb ftp://ubuntu.realss.cn/ubuntu/ precise-proposed main restricted universe multiverse
deb ftp://ubuntu.realss.cn/ubuntu/ precise-security main restricted universe multiverse
deb ftp://ubuntu.realss.cn/ubuntu/ precise-updates main restricted universe multiverse
deb-src ftp://ubuntu.realss.cn/ubuntu/ precise main restricted universe multiverse
deb-src ftp://ubuntu.realss.cn/ubuntu/ precise-backports main restricted universe multiverse
deb-src ftp://ubuntu.realss.cn/ubuntu/ precise-proposed main restricted universe multiverse
deb-src ftp://ubuntu.realss.cn/ubuntu/ precise-security main restricted universe multiverse
deb-src ftp://ubuntu.realss.cn/ubuntu/ precise-updates main restricted universe multiverse

成都市 电子科技大学更新服务器:

deb http://ubuntu.uestc.edu.cn/ubuntu/ precise main multiverse restricted universe
deb http://ubuntu.uestc.edu.cn/ubuntu/ precise-backports main multiverse restricted universe
deb http://ubuntu.uestc.edu.cn/ubuntu/ precise-proposed main multiverse restricted universe
deb http://ubuntu.uestc.edu.cn/ubuntu/ precise-security main multiverse restricted universe
deb http://ubuntu.uestc.edu.cn/ubuntu/ precise-updates main multiverse restricted universe
deb-src http://ubuntu.uestc.edu.cn/ubuntu/ precise main multiverse restricted universe
deb-src http://ubuntu.uestc.edu.cn/ubuntu/ precise-backports main multiverse restricted universe
deb-src http://ubuntu.uestc.edu.cn/ubuntu/ precise-proposed main multiverse restricted universe
deb-src http://ubuntu.uestc.edu.cn/ubuntu/ precise-security main multiverse restricted universe
deb-src http://ubuntu.uestc.edu.cn/ubuntu/ precise-updates main multiverse restricted universe

ssh-keygen的使用方法

一、概述

1、就是为了让两个linux机器之间使用ssh不需要用户名和密码。采用了数字签名RSA或者DSA来完成这个操作

2、模型分析

假设 A (192.168.20.59)为客户机器,B(192.168.20.60)为目标机;

要达到的目的:
A机器ssh登录B机器无需输入密码;
加密方式选 rsa|dsa均可以,默认dsa

二、具体操作流程

单向登陆的操作过程(能满足上边的目的):
1、登录A机器
2、ssh-keygen -t [rsa|dsa],将会生成密钥文件和私钥文件 id_rsa,id_rsa.pub或id_dsa,id_dsa.pub
3、将 .pub 文件复制到B机器的 .ssh 目录, 并 cat id_dsa.pub >> ~/.ssh/authorized_keys
4、大功告成,从A机器登录B机器的目标账户,不再需要密码了;(直接运行 #ssh 192.168.20.60 )

双向登陆的操作过程:

1、ssh-keygen做密码验证可以使在向对方机器上ssh ,scp不用使用密码.具体方法如下:
2、两个节点都执行操作:#ssh-keygen -t rsa
然后全部回车,采用默认值.

3、这样生成了一对密钥,存放在用户目录的~/.ssh下。
将公钥考到对方机器的用户目录下 ,并将其复制到~/.ssh/authorized_keys中(操作命令:#cat id_dsa.pub >> ~/.ssh/authorized_keys )。

4、设置文件和目录权限:

设置authorized_keys权限
$ chmod 600 authorized_keys
设置.ssh目录权限
$ chmod 700 -R .ssh

5、要保证.ssh和authorized_keys都只有用户自己有写权限。否则验证无效。(今天就是遇到这个问题,找了好久问题所在),其实仔细想想,这样做是为了不会出现系统漏洞。

我从20.60去访问20.59的时候会提示如下错误:

[java] view plaincopy

The authenticity of host ‘192.168.20.59 (192.168.20.59)’ can’t be established.
RSA key fingerprint is 6a:37:c0:e1:09:a4:29:8d:68:d0:ca:21:20:94:be:18.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘192.168.20.59’ (RSA) to the list of known hosts.
root@192.168.20.59’s password:
Permission denied, please try again.
root@192.168.20.59’s password:
Permission denied, please try again.
root@192.168.20.59’s password:
Permission denied (publickey,gssapi-with-mic,password).

三、总结注意事项

1、文件和目录的权限千万别设置成chmod 777.这个权限太大了,不安全,数字签名也不支持。我开始图省事就这么干了

2、生成的rsa/dsa签名的公钥是给对方机器使用的。这个公钥内容还要拷贝到authorized_keys

3、linux之间的访问直接 ssh 机器ip

4、某个机器生成自己的RSA或者DSA的数字签名,将公钥给目标机器,然后目标机器接收后设定相关权限(公钥和authorized_keys权限),这个目标机就能被生成数字签名的机器无密码访问了

maven

http://marshal.easymorse.com/archives/1212

http://marshal.easymorse.com/archives/2644
http://devbbs.doit.com.cn/thread-11232-1-1.html

http://devbbs.doit.com.cn/forum.php?mod=viewthread&tid=44055

http://blog.jeoygin.org/2012/03/hdfs-source-analysis-1-datanode-overview.html
http://hi.baidu.com/yangzhibin_bai/blog/item/1cd781ddfeffbad18d1029d1.html

http://devbbs.doit.com.cn/forum.php?mod=viewthread&tid=11232

http://devbbs.doit.com.cn/forum.php?mod=viewthread&tid=50067

http://devbbs.doit.com.cn/forum.php?mod=viewthread&tid=50199
http://juvenshun.iteye.com/blog/305865

在Linux下安装Nexus仓库管理器

1. 获得root权限
[ferrari@localhost ~]$ su –

2. 下载nexus集成版
[root@localhost ~]# cd /usr/local
[root@localhost local]# wget http://nexus.sonatype.org/downloads/nexus-oss-webapp-1.6.0-bundle.zip

3. 解压缩
[root@localhost local]# unzip nexus-oss-webapp-1.6.0-bundle.zip
[root@localhost local]# rm -rf nexus-oss-webapp-1.6.0-bundle.zip
[root@localhost local]# ln -s nexus-oss-webapp-1.6.0 nexus

4. 设置为系统服务
[root@localhost local]# cd /etc/init.d
[root@localhost init.d]# cp /usr/local/nexus/bin/jsw/linux-x86-32/nexus ./nexus
[root@localhost init.d]# chmod 755 nexus
[root@localhost init.d]# chkconfig –add nexus
[root@localhost init.d]# chkconfig –levels 345 nexus on

5. 编辑/etc/init.d/nexus的文本
5.1. 添加以下变量
NEXUS_HOME=/usr/local/nexus
PLATFORM=linux-x86-32
PLATFORM_DIR=”${NEXUS_HOME}/bin/jsw/${PLATFORM}”
5.2. 修改以下变量
WRAPPER_CMD=”${PLATFORM_DIR}/wrapper”
WRAPPER_CONF=”${PLATFORM_DIR}/../conf/wrapper.conf”

PIDDIR=”${NEXUS_HOME}”

6. 启动服务
[root@localhost init.d]# service nexus start

7. 处理SELinux警告
7.1. 启动之后,会受到一个SELinux警告:
SELinux is preventing java from loading /usr/local/nexus-oss-webapp-1.6.0/bin/jsw/linux-x86-32/lib/libwrapper.so which requires text relocation.
7.2. 按照其中提示运行以下命令:
[root@localhost init.d]# chcon -t textrel_shlib_t ‘/usr/local/nexus-oss-webapp-1.6.0/bin/jsw/linux-x86-32/lib/libwrapper.so’

8. 启动之后,打开以下网址可以看到Nexus网页界面。
http://localhost:8081/nexus/index.html
默认管理员用户名/密码: admin/admin123

9. 在防火墙设置中添加8081端口的TCP协议,局域网内其他机器也可以访问该网页界面。
http://repository-server:8081/nexus/index.html

chkconfig小bug解决

背景:

ubuntu 11.04

3天前刚执行了一次自动更新

Bug:

不知道是不是更新的原因,每当运行chkconfig –add或者–del命令时,就出现如下错误提示:

/sbin/insserv: 没有那个文件或目录

已尝试但无效的方法:

重新安装insserv,无效

重新安装chkconfig,无效

解决方案/过程:

(1) 查找insserv安装位置

whereis insserv

结果如下:

insserv: /etc/insserv /etc/insserv.conf /usr/lib/insserv /usr/share/insserv /usr/share/man/man8/insserv.8.gz

可以发现insserv根本不在/sbin/目录下,而是在/usr/lib/insserv/目录下。

(2) 查找chkconfig安装位置

whereis chkconfig

结果如下:

chkconfig: /sbin/chkconfig /usr/share/man/man8/chkconfig.8.gz

(3) 编辑chkconfig文件

sudo gedit /sbin/chkconfig

找到第176行:# my @i = (“/sbin/insserv”);

将引号内的内容改为“/usr/lib/insserv/insserv”.

保存即可。

Java NIO原理和使用

Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。

Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

Java NIO出现不只是一个技术性能的提高,你会发现网络上到处在介绍它,因为它具有里程碑意义,从JDK1.4开始,Java开始提高性能相关的功能,从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。

如果你至今还是在怀疑Java的性能,说明你的思想和观念已经完全落伍了,Java一两年就应该用新的名词来定义。从JDK1.5开始又要提供关于线程、并发等新性能的支持,Java应用在游戏等适时领域方面的机会已经成熟,Java在稳定自己中间件地位后,开始蚕食传统C的领域。

本文主要简单介绍NIO的基本原理,在下一篇文章中,将结合Reactor模式和著名线程大师Doug Lea的一篇文章深入讨论。

NIO主要原理和适用。

NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。

了解了这个基本原理,我们结合代码看看使用,在使用上,也在分两个方向,一个是线程处理,一个是用非线程,后者比较简单,看下面代码:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.net.*;
import java.util.*;
/**
*
* @author Administrator
* @version
*/

public class NBTest {

  /** Creates new NBTest */
  public NBTest()
  {
  }

  public void startServer() throws Exception
  {
  int channels = 0;
  int nKeys = 0;
  int currentSelector = 0;

  //使用Selector
  Selector selector = Selector.open();

  //建立Channel 并绑定到9000端口
  ServerSocketChannel ssc = ServerSocketChannel.open();
  InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),9000);
  ssc.socket().bind(address);

  //使设定non-blocking的方式。
  ssc.configureBlocking(false);

  //向Selector注册Channel及我们有兴趣的事件
  SelectionKey s = ssc.register(selector, SelectionKey.OP_ACCEPT);
  printKeyInfo(s);

  while(true) //不断的轮询
  {
    debug(“NBTest: Starting select”);

    //Selector通过select方法通知我们我们感兴趣的事件发生了。
    nKeys = selector.select();
    //如果有我们注册的事情发生了,它的传回值就会大于0
    if(nKeys > 0)
    {
      debug(“NBTest: Number of keys after select operation: ” +nKeys);

      //Selector传回一组SelectionKeys
      //我们从这些key中的channel()方法中取得我们刚刚注册的channel。
      Set selectedKeys = selector.selectedKeys();
      Iterator i = selectedKeys.iterator();
      while(i.hasNext())
      {
         s = (SelectionKey) i.next();
         printKeyInfo(s);
         debug(“NBTest: Nr Keys in selector: ” +selector.keys().size());

         //一个key被处理完成后,就都被从就绪关键字(ready keys)列表中除去
         i.remove();
         if(s.isAcceptable())
         {
           // 从channel()中取得我们刚刚注册的channel。
           Socket socket = ((ServerSocketChannel)s.channel()).accept().socket();
           SocketChannel sc = socket.getChannel();

           sc.configureBlocking(false);
           sc.register(selector, SelectionKey.OP_READ |SelectionKey.OP_WRITE);
                      System.out.println(++channels);
         }
         else
         {
           debug(“NBTest: Channel not acceptable”);
         }
      }
   }
   else
   {
      debug(“NBTest: Select finished without any keys.”);
   }

  }

}

private static void debug(String s)
{
  System.out.println(s);
}

private static void printKeyInfo(SelectionKey sk)
{
  String s = new String();

  s = “Att: ” + (sk.attachment() == null ? “no” : “yes”);
  s += “, Read: ” + sk.isReadable();
  s += “, Acpt: ” + sk.isAcceptable();
  s += “, Cnct: ” + sk.isConnectable();
  s += “, Wrt: ” + sk.isWritable();
  s += “, Valid: ” + sk.isValid();
  s += “, Ops: ” + sk.interestOps();
  debug(s);
}

/**
* @param args the command line arguments
*/
public static void main (String args[])
{
  NBTest nbTest = new NBTest();
  try
  {
    nbTest.startServer();
  }
    catch(Exception e)
  {
    e.printStackTrace();
  }
}

}

这是一个守候在端口9000的noblock server例子,如果我们编制一个客户端程序,就可以对它进行互动操作,或者使用telnet 主机名 90000 可以链接上。

通过仔细阅读这个例程,相信你已经大致了解NIO的原理和使用方法,下一篇,我们将使用多线程来处理这些数据,再搭建一个自己的Reactor模式。

JAVA NIO 简介

http://www.iteye.com/topic/834447

1. 基本 概念
IO 是主存和外部设备 ( 硬盘、终端和网络等 ) 拷贝数据的过程。 IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成。
所有语言运行时系统提供执行 I/O 较高级别的工具。 (c 的 printf scanf,java 的面向对象封装 )
2. Java 标准 io 回顾
Java 标准 IO 类库是 io 面向对象的一种抽象。基于本地方法的底层实现,我们无须关注底层实现。 InputStreamOutputStream( 字节流 ) :一次传送一个字节。 ReaderWriter( 字符流 ) :一次一个字符。
3. nio 简介
nio 是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下:
– 为所有的原始类型提供 (Buffer) 缓存支持。
– 字符集编码解码解决方案。
– Channel :一个新的原始 I/O 抽象。
– 支持锁和内存映射文件的文件访问接口。
– 提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O 。
本文将围绕这几个特性进行学习和介绍。
4. Buffer&Chanel
Channel 和 buffer 是 NIO 是两个最基本的数据类型抽象。
Buffer:
– 是一块连续的内存块。
– 是 NIO 数据读或写的中转地。
Channel:
– 数据的源头或者数据的目的地
– 用于向 buffer 提供数据或者读取 buffer 数据 ,buffer 对象的唯一接口。
– 异步 I/O 支持

图1:channel和buffer关系

例子 1:CopyFile.java:
Java代码
package sample;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class CopyFile {
public static void main(String[] args) throws Exception {
String infile = “C:\copy.sql”;
String outfile = “C:\copy.txt”;
// 获取源文件和目标文件的输入输出流
FileInputStream fin = new FileInputStream(infile);
FileOutputStream fout = new FileOutputStream(outfile);
// 获取输入输出通道
FileChannel fcin = fin.getChannel();
FileChannel fcout = fout.getChannel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
// clear方法重设缓冲区,使它可以接受读入的数据
buffer.clear();
// 从输入通道中将数据读到缓冲区
int r = fcin.read(buffer);
// read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1
if (r == -1) {
break;
}
// flip方法让缓冲区可以将新读入的数据写入另一个通道
buffer.flip();
// 从输出通道中将数据写入缓冲区
fcout.write(buffer);
}
}
}

其中 buffer 内部结构如下 ( 下图拷贝自资料 ):

图2:buffer内部结构
一个 buffer 主要由 position,limit,capacity 三个变量来控制读写的过程。此三个变量的含义见如下表格:
参数
写模式
读模式
position
当前写入的单位数据数量。
当前读取的单位数据位置。
limit
代表最多能写多少单位数据和容量是一样的。
代表最多能读多少单位数据,和之前写入的单位数据量一致。
capacity
buffer 容量
buffer 容量
Buffer 常见方法:
flip(): 写模式转换成读模式
rewind() :将 position 重置为 0 ,一般用于重复读。
clear() :清空 buffer ,准备再次被写入 (position 变成 0 , limit 变成 capacity) 。
compact(): 将未读取的数据拷贝到 buffer 的头部位。
mark() 、 reset():mark 可以标记一个位置, reset 可以重置到该位置。
Buffer 常见类型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、 ShortBuffer 。
channel 常见类型 :FileChannel 、 DatagramChannel(UDP) 、 SocketChannel(TCP) 、 ServerSocketChannel(TCP)
在本机上面做了个简单的性能测试。我的笔记本性能一般。 ( 具体代码可以见附件。见 nio.sample.filecopy 包下面的例子 ) 以下是参考数据:
– 场景 1 : Copy 一个 370M 的文件
– 场景 2: 三个线程同时拷贝,每个线程拷贝一个 370M 文件

场景
FileInputStream+
FileOutputStream
FileInputStream+
BufferedInputStream+
FileOutputStream
ByteBuffer+
FileChannel
MappedByteBuffer
+FileChannel
场景一时间 ( 毫秒 )
25155
17500
19000
16500
场景二时间 ( 毫秒 )
69000
67031
74031
71016
5. nio.charset
字符编码解码 : 字节码本身只是一些数字,放到正确的上下文中被正确被解析。向 ByteBuffer 中存放数据时需要考虑字符集的编码方式,读取展示 ByteBuffer 数据时涉及对字符集解码。
Java.nio.charset 提供了编码解码一套解决方案。
以我们最常见的 http 请求为例,在请求的时候必须对请求进行正确的编码。在得到响应时必须对响应进行正确的解码。
以下代码向 baidu 发一次请求,并获取结果进行显示。例子演示到了 charset 的使用。
例子 2BaiduReader.java
Java代码
package nio.readpage;

import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.net.InetSocketAddress;
import java.io.IOException;
public class BaiduReader {
private Charset charset = Charset.forName(“GBK”);// 创建GBK字符集
private SocketChannel channel;
public void readHTMLContent() {
try {
InetSocketAddress socketAddress = new InetSocketAddress(
“www.baidu.com”, 80);
//step1:打开连接
channel = SocketChannel.open(socketAddress);
//step2:发送请求,使用GBK编码
channel.write(charset.encode(“GET ” + “/ HTTP/1.1” + “rnrn”));
//step3:读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);// 创建1024字节的缓冲
while (channel.read(buffer) != -1) {
buffer.flip();// flip方法在读缓冲区字节操作之前调用。
System.out.println(charset.decode(buffer));
// 使用Charset.decode方法将字节转换为字符串
buffer.clear();// 清空缓冲
}
} catch (IOException e) {
System.err.println(e.toString());
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
}
}
}
}
public static void main(String[] args) {
new BaiduReader().readHTMLContent();
}
}

6. 非阻塞 IO
关于非阻塞 IO 将从何为阻塞、何为非阻塞、非阻塞原理和异步核心 API 几个方面来理解。
何为阻塞?
一个常见的网络 IO 通讯流程如下 :

图3:网络通讯基本过程
从该网络通讯过程来理解一下何为阻塞 :
在以上过程中若连接还没到来,那么 accept 会阻塞 , 程序运行到这里不得不挂起, CPU 转而执行其他线程。
在以上过程中若数据还没准备好, read 会一样也会阻塞。
阻塞式网络 IO 的特点:多线程处理多个连接。每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部为准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。且大部分进程上下文切换可能是无意义的。比如假设一个线程监听一个端口,一天只会有几次请求进来,但是该 cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。

何为非阻塞?
下面有个隐喻:
一辆从 A 开往 B 的公共汽车上,路上有很多点可能会有人下车。司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好?
1. 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车。 ( 类似阻塞式 )
2. 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车。 ( 类似非阻塞 )
很显然,每个人要到达某个目的地可以认为是一个线程,司机可以认为是 CPU 。在阻塞式里面,每个线程需要不断的轮询,上下文切换,以达到找到目的地的结果。而在非阻塞方式里,每个乘客 ( 线程 ) 都在睡觉 ( 休眠 ) ,只在真正外部环境准备好了才唤醒,这样的唤醒肯定不会阻塞。
非阻塞的原理
把整个过程切换成小的任务,通过任务间协作完成。
由一个专门的线程来处理所有的 IO 事件,并负责分发。
事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的进程切换。
以下是异步 IO 的结构:

图4:非阻塞基本原理

Reactor 就是上面隐喻的售票员角色。每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。
异步 IO 核心 API
Selector
异步 IO 的核心类,它能检测一个或多个通道 (channel) 上的事件,并将事件分发出去。
使用一个 select 线程就能监听多个通道上的事件,并基于事件驱动触发相应的响应。而不需要为每个 channel 去分配一个线程。
SelectionKey
包含了事件的状态信息和时间对应的通道的绑定。
例子 1 单线程实现监听两个端口。 ( 见 nio.asyn 包下面的例子。 )
例子 2 NIO 线程协作实现资源合理利用。 (wait,notify) 。 ( 见 nio.asyn.multithread 下的例子 )

教是最好的学

在部门以及团队内部鼓励大家分享交流时,会提到一个观点:教是最好的学——如何让自己学习并掌握好一门知识、一种技能,最好的方法就是把这门知识、技能去教授给别人。

如何学习,如何让自己掌握新的知识技能,每个人都有自己的方法,但是,教授是最快速直接和双赢的方法。

记得第一次去苏州给销售、客服培训业务知识时,备课过程中发现对业务的理解是那么苍白,随便一个知识点扩展就不知道原理,然后用一个多星期的个人时间去进行知识点的学习深造;
记得第一次当导师,很多习以为常的业务点,无法清晰的讲出来原理让新员工听明白,然后自己不断的找生活的类似的例子做比喻说明,不知不觉中锻炼了表达能力、拓展思路;
记得第一次当公司讲师,很多资深的专业人士一针见血的提出问题并把我晾在台上,自己想办法圆场、致歉,请专家表态,恨不得永不登台,然后课后查漏补缺,一步步提高自己……

相信很多同事也有类似的经历,当你去教给别人知识、业务的时候,往往能发现自己的不足,或者通过沟通交流,让自己触类旁通,学到更多的东西。

这就是我要描述的最好的学习方法:教是最好的学。

大家可以自己尝试一下:
你阅读一篇技术文档,抱着自己了解熟悉的态度看两个小时,你能学到什么?
同样,如果要求你两个小时后,给领导或同事当众演讲,你能学到什么?

如果你以一个老师的角色来学习,那么你会更加专心,你会尝试理解文档的系统架构,你会尝试用一些例子或者比喻去解答一些技术逻辑或现象,你会尝试提炼出文档的核心要素,你会尝试去重构这篇文档。

教是最好的学是一个完整的循环:学习-理解-重构-讲解-沟通-反思-再学习。

1、 学习:首先你会有一个学习的过程,把一门知识通过自己的方法进行学习掌握。

2、 理解:并不是看过书,知道皮毛就可以去交给别人,做到教的程度,你还要对所学的东西做一个深入的理解,把书本上的语言和思路,转化为自己的语言和思路,把技能的方法转化为自己的方法。

3、 重构:自己制作PPT,把别人的道理转化为自己的道理。借用一句经典的话:当你对一个行业有了自己的看法和认识,而不是生搬硬套各种原理定律时,就表明你对这个行业入门了。

4、 讲解:把自己对这门知识、技能的理解讲出来,是学有小成的概念。别人的道理经过你的吸收、理解,最后能表达出来的东西,才是你的。比如做菜,你看了很多菜谱和美食节目,最后自己下厨炒出来的,才是自己学习的成果。

5、 沟通:你在培训、学习、例会、评审等各类场合讲出来,无论面对的是新员工、同事、高级工程师、资深专家还是领导、外部客户等等,你表达了自己的看法,肯定会得到很多反应:求学、质疑、刁难、好奇、奇思妙想……通过解答问题,或者干脆承认自己没在某方向下功夫,你会对这门知识的理解程度拓展和深入很多。这是对自己掌握知识的最好审查。

6、 反思:通过教学过程沟通,得到一个结果后反思自己所学,会发现很多薄弱环节和新鲜思路,以此形成个人小结。

7、 新的循环:学习-理解-重构-讲解-沟通-反思-再学习。

在实际的工作中,个人也是这样应用的。

1、 比如测试部有一名新的LTM,对测试流程不太熟悉,那么就安排新LTM在测试部二营培训中,专门给新员工讲解测试流程。那么无论是基于压力,还是基于责任,或者基于面子,新LTM肯定会在课前尽心尽力的去学习掌握测试流程。然后通过课程过程的讲述,以及和大家的互动,肯定能让自己对测试流程的理解深入很多。

2、 讲解需求:原来都是测试LTM给测试工程师讲解新的功能需求点,以及测试思路测试方法,结果效果不好,技术的东西总是枯燥的,测试工程师听的昏昏欲睡。后来我们就反过来,让测试工程师学习之后去教给LTM,去讲给LTM听,这样能让学习效果最大化。

3、 同样的思路,我们在一步步落实,让开发人员去给需求人员讲解需求——需求人员对需求再清楚,如果开发人员不理解也没用。反过来做,效果也是最大化。

4、 个人提升方面也是:比如让一个对H323不熟悉的人,要求一个月后给大家讲解H323协议,相信听课的人,能或多或少学到一些知识,但是收获最大的,还是讲课的人——他费心费力的去学习,制作ppt重构,用自己的语言去讲解知识,并做课堂解答。

以上就是我对学习的理解。所以我才会言传身教的告诉大家,多出来分享你的知识和技能,这是双赢的事情,第一你能让更多人获得技能,从而让自己解放,第二,你能在整个过程中,收获很多新的知识、思路、灵感。

教是最好的学,在整个过程中,实际上获得最大收获的,是站出来教学的人。你付出的越多,你收获的自然也就越多!