nio:
一个简单的nioserver。启动后,在浏览器输入:http://localhost:1234。就可以看到http的请求。
public class Test<T> implements Callable<T> {
private Selector selector;// 创建全局selector
public Test() throws IOException {
selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(1234));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public T call() throws Exception {
for (;;) {
if (selector.select(1000) == 0) {
continue;
}
Iterator<SelectionKey> keyIterator = selector.selectedKeys()
.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
SocketChannel clientChannel = ((ServerSocketChannel) key
.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel
.register(key.selector(), SelectionKey.OP_READ);
}
if (key.isReadable()) {
// 获得与客户端通信的信道
SocketChannel clientChannel = (SocketChannel) key.channel();
// 得到并重置缓冲区的主要索引值
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
// 读取信息获得读取的字节数
long bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// 没有读取到内容的情况
clientChannel.close();
} else {
// 将缓冲区准备为数据传出状态
buffer.flip();
// 将获得字节字符串(使用Charset进行解码)
String receivedString = Charset.forName("utf-8")
.newDecoder().decode(buffer).toString();
// 控制台打印出来
System.out.println("接收到信息:" + receivedString);
// 准备发送的文本
String sendString = "你好,客户端. 已经收到你的信息" + receivedString;
// 将要发送的字符串编码(使用Charset进行编码)后再进行包装
buffer = ByteBuffer.wrap(sendString.getBytes("utf-8"));
// 发送回去
clientChannel.write(buffer);
// 设置为下一次读取或是写入做准备
key.interestOps(SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
}
}
keyIterator.remove();
}
}
}
public static void main(String args[]) throws IOException, Exception {
new Test().call();
}
}
Reactor模式:
1.Reactor 负责响应IO事件,一旦发生,广播发送给相应的Handler去处理。
2.Handler 是负责非堵塞行为。
public class Reactor implements Runnable{
final Selector selector;
final ServerSocketChannel serverSocket;
ExecutorService pool = Executors.newFixedThreadPool(3);
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(port);
serverSocket.socket().bind(address);
serverSocket.configureBlocking(false);
// 向selector注册该channel
SelectionKey sk = serverSocket.register(selector,
SelectionKey.OP_ACCEPT);
// 利用sk的attache功能绑定Acceptor 如果有事情,触发Acceptor
sk.attach(new Acceptor());
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable) (k.attachment());
r.run();
}
public void run() {
try {
while (true) {
int temp = selector.select();
Iterator<SelectionKey> keyIter = selector.selectedKeys()
.iterator();
while (keyIter.hasNext()) {
SelectionKey key = keyIter.next();
keyIter.remove();
dispatch(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
class Acceptor implements Runnable {
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
pool.execute(new SocketReadHandler(selector, c));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws IOException {
new Reactor(1234).run();
}
}
public class SocketReadHandler implements Runnable {
public static Logger logger = Logger.getLogger("SocketReadHandler");
final SocketChannel sc;
final SelectionKey sk;
ByteBuffer input = ByteBuffer.allocate(1024);
ByteBuffer output = ByteBuffer.allocate(1024);
static final int READING = 0, SENDING = 1;
int state = READING;
public SocketReadHandler(Selector sel, SocketChannel sc) throws IOException {
this.sc = sc;
sc.configureBlocking(false);
sk = sc.register(sel, 0);
// 将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。
// 参看dispatch(SelectionKey k)
sk.attach(this);
// 同时将SelectionKey标记为可读,以便读取。
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void send() throws IOException {
sc.write(output);
sk.cancel();
}
private void read() throws IOException {
long bytesRead = sc.read(input);
if (bytesRead == -1) {
sc.close();
} else {
input.flip();
String receivedString = Charset.forName("utf-8").newDecoder()
.decode(input).toString();
// 控制台打印出来
System.out.println("接收到信息:" + receivedString);
state = SENDING;
sk.interestOps(SelectionKey.OP_WRITE);
}
}
}
上面代码改编自《Reactor模式和NIO》,详见参考资料。
SocketChannel的attach功能,将Hanlder和可能会发生事件的channel链接在一起,当发生事件时,可以立即触发相应链接的Handler。增加了一个线程池
Mina的Reator:
与传统的单个Reactor模式实现不同,mina中采用了Multiple Reactor的方式。NioSocketAcceptor和NioProcessor使用不同selector,能够更加充分的榨取服务器的性能。
acctptor主要负责
1. 绑定一个/多个端口,开始监听
2. 处理客户端的建链请求
3. 关闭一个/多个监听端口
processor主要负责
1. 接受客户端发送的数据,并转发给业务逻辑成处理
2. 发送数据到客户端
mina的reactor模式详见mina1.0.10的源码:http://mina.apache.org/dyn/closer.cgi/mina/1.0.10/mina-1.0.10.zip
参考资料:
uniseraph:http://uniseraph.iteye.com/blog/228221——《【mina指南】mina中的reactor模式(一)》
uniseraph:http://uniseraph.iteye.com/blog/229130——《【mina指南】mina中的reactor模式(二)》
何杨:http://www.blogjava.net/heyang/archive/2011/01/03/342193.html——《Java NIO 服务器端简单实现例子》
板桥里人:http://www.jdon.com/concurrent/reactor.htm——《Reactor模式和NIO》