1. 为什么需要NIO?
使用传统的ServerSocket操作时会遇到accept()阻塞, BufferReader阻塞,以及大量的String垃圾,这是由于用来存储从BufferredReader中读入的String。同样问题在发送消息中也存在。传统的解决方法是使用线程池,即当服务器端监听到客户连接时,就为客户创建一个线程,并将该线程放入线程池中,在客户断开连接时,客户线程归还到线程池中。线程池使得服务器可以处理多个连接,但是同样带来开销,并且许多时间是浪费在阻塞的I/O上。
NIO的非阻塞I/O机制是围绕selector和channel来构建的。channel负责客户端和服务器端的通信,Selector是Channel的多路复用器。即Selector将传入的客户机请求分派到各自的请求处理程序中,比如连接事件,读取事件,写入事件。
2. NIO核心框架
-Buffer: 对比传统I/O不断浪费对象资源(String),NIO使用缓存区避免资源浪费。每一个非布尔型的基本类型都有一个缓存区。
-Charset:
通过CharsetEncoder和CharsetDecoder将CharBuffer转换成ByteBuffer.
-Channel:
替代数据流,提供读写Buffer的方式。
-Selector:
非阻塞I/O的核心,同时对多个SelectableChannel的I/O情况进行监控。
使用传统的ServerSocket操作时会遇到accept()阻塞, BufferReader阻塞,以及大量的String垃圾,这是由于用来存储从BufferredReader中读入的String。同样问题在发送消息中也存在。传统的解决方法是使用线程池,即当服务器端监听到客户连接时,就为客户创建一个线程,并将该线程放入线程池中,在客户断开连接时,客户线程归还到线程池中。线程池使得服务器可以处理多个连接,但是同样带来开销,并且许多时间是浪费在阻塞的I/O上。
NIO的非阻塞I/O机制是围绕selector和channel来构建的。channel负责客户端和服务器端的通信,Selector是Channel的多路复用器。即Selector将传入的客户机请求分派到各自的请求处理程序中,比如连接事件,读取事件,写入事件。
2. NIO核心框架
-Buffer: 对比传统I/O不断浪费对象资源(String),NIO使用缓存区避免资源浪费。每一个非布尔型的基本类型都有一个缓存区。
-Charset:
通过CharsetEncoder和CharsetDecoder将CharBuffer转换成ByteBuffer.
-Channel:
替代数据流,提供读写Buffer的方式。
-Selector:
非阻塞I/O的核心,同时对多个SelectableChannel的I/O情况进行监控。
3. C/S with NIO
Class Server{
main(){
//创建selector
Selector selector = Selector.open();
//创建Socket
ServerSocketChannel server = ServerSocketChannel.open();
//启动端口监听
InetSocketAddress ip = new InetSocketAddress(12345);
server.socket().bind(ip);
//注册selector
server.configureBlocking(false);
server.register(selector, SlectionKey.OP_ACCEPT);
while(true){
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
it.remove();
if(key.isAcceptable()){
ServerSocketChannel server2 = (ServerSocketChannel)
key.channel();
SocketChannel channel = server2.accpet();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable){
Socketchannel channel = (SocketChannel)key.channel();
//读数据
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
ByteBuffer buffer = ByteBuffer.allocate(50);
channel.read(buffer);
buffer.flip();
String msg = decoder.decode(buffer).toString();
//写数据
CharsetEncoder encoder = Charset.forName(“UTF-8”)
.newEncoder();
channel.write(encoder.encode(CharBuffer.wrap(msg)));
}
}
}
}
}
Class Client{
main(){
ClientThread client = new ClientThread();
client.run();
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String readLine;
while((readLine = sin.readLine())!=null){
client.send(readLine);
}
}
}
Class ClientThread{
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
Selector selector = null;
SocketChannel socket = null;
SelectionKey clientKey = null;
ClientThread(){
selector = Selector.open();
socket=SocketChannel.open();
socket.configureBlocking(false);
clientKey = socket.register(selector, SelectionKey.OP_CONNECT);
InetSocketAddress ip = new InetSocketAddress("localhost",12345);
socket.connect(ip);
}
run(){
while(true){
selector.select(1);
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
it.remove();
if(key.isConnectable()){
SocketChannel channel = (SocketChannel)key.channel();
channel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable()){
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(50);
channel.read(buffer);
buffer.flip();
String msg = decoder.decode(buffer).toString();
}
}
}
}
send(String msg){
SocketChannel client = (SocketChannel)clientKey.channel();
client.write(encoder.encode(CharBuffer.wrap(msg)));
}
}