2011年5月22日星期日

一个典型的非阻塞IO程序 收藏

http://blog.csdn.net/zouxinfox/archive/2007/04/20/1572288.aspx


一个典型的非阻塞IO程序 收藏

Java 非阻塞IO常用于高性能的服务器程序。对于阻塞式IO常常需要多个线程来处理客户端的请求,由于线程的开销较大,往往使服务器性能下降很快。而非阻塞IO只需几个线程就可以胜任大量的请求。对于p2p软件(例如BT软件),也常常使用非阻塞IO,来实现文件交换。

下面是一个典型的非阻塞IO程序。客户端向服务器端发起10个连接,服务器端向每个客户端发送”Hello”,并打印出来。


服务器端程序:



import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Set;



public class Server {

// 服务器端口

public static int port = 9994;



public Server() {

init();

}



public void init() {

Selector selector = null;

try {

// 获得Selector实例

selector = Selector.open();

// 获得ServerSocketChannel实例

ServerSocketChannel serverChannel = ServerSocketChannel.open();

InetSocketAddress add = new InetSocketAddress("localhost", port);

// 设为非阻塞模式,默认为阻塞模式

serverChannel.configureBlocking(false);

// channel与一个InetSocketAddress绑定

serverChannel.socket().bind(add);

// 向selector注册

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

} catch (IOException e) {

e.printStackTrace();

return;

}

while (true) {

try {

// 如果没有准备好的channel,就在这一直阻塞

// 注意刚启动时,没有客户端与服务器端连接,会阻塞

selector.select();

} catch (IOException e) {



e.printStackTrace();

break;

}

// 返回已经就绪的SelctionKey,然后迭代执行

Set readyKeys = selector.selectedKeys();

for (Iterator it = readyKeys.iterator(); it.hasNext();) {

SelectionKey key = (SelectionKey) it.next();

// 为防止重复迭代要执行remove,在执行selector.select()时,会自动加入去掉的key

it.remove();

try {

// 对应于注册的OP_ACCEPT管道,在这里即ServerSocketChannel

if (key.isAcceptable()) {

ServerSocketChannel server = (ServerSocketChannel) key

.channel();

// 由于ServerSocketChannel为非阻塞模式,因此不会在这阻塞

SocketChannel client = server.accept();

client.configureBlocking(false);

// 表明接受到一个客户端连接,将其注册到selector

// 执行selector.select()时可以自动选一个channel

client.register(selector, SelectionKey.OP_WRITE);

// 对应于注册的OP_WRITE管道,在这里即SocketChannel

} else if (key.isWritable()) {

SocketChannel client = (SocketChannel) key.channel();

// 开辟20个字节的缓冲区

ByteBuffer buffer = ByteBuffer.allocate(20);

String str = "hello";

// 将"hello"封装到buffer

buffer = ByteBuffer.wrap(str.getBytes());

// 写入客户端

client.write(buffer);

// 写完hello后取消通道的注册

key.cancel();



}

} catch (IOException e) {

e.printStackTrace();

key.cancel();

try {

// 关闭通道

key.channel().close();

} catch (IOException e1) {

e.printStackTrace();

}

}

}// end for

}// end while



}



public static void main(String[] args) {



Server server = new Server();



}



}


客户端程序:

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.SocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SocketChannel;



public class Client {

public Client() {

init();

}



public void init() {

try {

SocketAddress add = new InetSocketAddress("localhost", Server.port);

// 返回SocketChannel实例,并绑定SocketAddress

SocketChannel client = SocketChannel.open(add);

client.configureBlocking(false);

ByteBuffer buffer = ByteBuffer.allocate(20);

// 从通道中读取

client.read(buffer);

// 为读取做准备

buffer.flip();

String result = "";

// 每次读一个字符

while (buffer.hasRemaining())

result += String.valueOf((char) buffer.get());

System.out.println(result);

client.close();



} catch (IOException e) {



e.printStackTrace();

}

}



public static void main(String[] args) {



Client client=null;

for(int i=0;i<10;i++){

client = new Client();

System.out.println("client "+i+" has connected");

}



}



}


运行结果:

hello

client 0 has connected

hello

client 1 has connected

hello

client 2 has connected

hello

client 3 has connected

hello

client 4 has connected

hello

client 5 has connected

hello

client 6 has connected

hello

client 7 has connected

hello

client 8 has connected

hello

client 9 has connected

没有评论:

发表评论