Java NIO-ServerSocketChannel与Soc
1.NIO
nio异步阻塞IO。
阻塞:应用程序获取网络资源一直等待传输完成。
非阻塞:应用程序直接获取准备好的数据。
同步:程序直接参与IO读写操作,并且程序直接阻塞在某一个方法上,直到数据准备就绪获取数据。
异步:所有IO读写操作都由操作系统处理,程序不直接参与IO,操作系统完成读写向程序发送通知,程序直接取走数据。
同步、异步指的是server的执行方式,阻塞是指具体技术、接收数据的方式、状态。
nio在没有收到数据的情况下不会有线程去保持连接这与bio不同,bio的SocketServer在接到连接就会去开启一个线程维持,bio使用一个队列保存连接用Selector不断轮询查找有可读取数据的连接在有可读取数据的连接的时候才会开启一个线程去读取数据,读取数据还是要nio直接参与并不像AIO是操作系统读取数据。
nio与传统io(bio)差别较大,nio由Buffer缓冲区、Channel管道、Selector选择器等三个对象组成应用部分。
Buffer缓冲区:所有数据都是用Buffer缓冲区读写,Bufeer实质上是一个数组,包含ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。

Channel管道:同时用于读写。
Selector选择器:类似一个Master的管理者,管理Channel。Channel注册到Selector时,其会给每个Channel分配一个key,Selector在轮询到Channel准备好数据后通过key找到Channel进行相应的io操作。Selector检测Channel的四个状态SelectionKey.

/**
* Operation-set bit for read operations.
*
* <p> Suppose that a selection key's interest set contains
* <tt>OP_READ</tt> at the start of a <a
* href="Selector.html#selop">selection operation</a>. If the selector
* detects that the corresponding channel is ready for reading, has reached
* end-of-stream, has been remotely shut down for further reading, or has
* an error pending, then it will add <tt>OP_READ</tt> to the key's
* ready-operation set and add the key to its selected-key set. </p>
*/
public static final int OP_READ = 1 << 0;
/**
* Operation-set bit for write operations.
*
* <p> Suppose that a selection key's interest set contains
* <tt>OP_WRITE</tt> at the start of a <a
* href="Selector.html#selop">selection operation</a>. If the selector
* detects that the corresponding channel is ready for writing, has been
* remotely shut down for further writing, or has an error pending, then it
* will add <tt>OP_WRITE</tt> to the key's ready set and add the key to its
* selected-key set. </p>
*/
public static final int OP_WRITE = 1 << 2;
/**
* Operation-set bit for socket-connect operations.
*
* <p> Suppose that a selection key's interest set contains
* <tt>OP_CONNECT</tt> at the start of a <a
* href="Selector.html#selop">selection operation</a>. If the selector
* detects that the corresponding socket channel is ready to complete its
* connection sequence, or has an error pending, then it will add
* <tt>OP_CONNECT</tt> to the key's ready set and add the key to its
* selected-key set. </p>
*/
public static final int OP_CONNECT = 1 << 3;
/**
* Operation-set bit for socket-accept operations.
*
* <p> Suppose that a selection key's interest set contains
* <tt>OP_ACCEPT</tt> at the start of a <a
* href="Selector.html#selop">selection operation</a>. If the selector
* detects that the corresponding server-socket channel is ready to accept
* another connection, or has an error pending, then it will add
* <tt>OP_ACCEPT</tt> to the key's ready set and add the key to its
* selected-key set. </p>
*/
public static final int OP_ACCEPT = 1 << 4;
2.NIOServer
NIOServer .java
package cn.thinglin.nio;
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;
public class NIOServer implements Runnable{
public Selector mSelector;
public static final int PORT = 18881;
public NIOServer(int port){
ServerSocketChannel ssc = null;
try {
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);//非阻塞模式
mSelector = Selector.open();
ssc.bind(new InetSocketAddress(port));
ssc.register(mSelector, SelectionKey.OP_ACCEPT); //监听连接请求
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean accept(SelectionKey key){
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
try {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
ByteBuffer writeBuf = ByteBuffer.allocate(1024);
sc.write(writeBuf.wrap("连我干啥".getBytes("UTF-8")));
sc.register(mSelector, SelectionKey.OP_READ); //监听读
} catch (IOException e) {
e.printStackTrace();
try {
ssc.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return true;
}
private boolean read(SelectionKey key){
SocketChannel sc = (SocketChannel) key.channel();
try {
ByteBuffer readBuf = ByteBuffer.allocate(1024);
sc.read(readBuf);
String msg = new String(readBuf.array(),"UTF-8").trim();
System.out.println("客户端来信:"+msg);
ByteBuffer writeBuf = ByteBuffer.allocate(1024);
sc.write(writeBuf.wrap("打汉堡啊!over".getBytes("UTF-8")));
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
@Override
public void run() {
while(true){
try {
this.mSelector.select();
Iterator<SelectionKey> iterator = this.mSelector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
//监听连接
if(key.isAcceptable()){
accept(key);
}
//监听读
if(key.isReadable()){
read(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NIOServer nioServer = new NIOServer(PORT);
new Thread(nioServer).start();
}
}
3,NIOClient
NIOClient.java
package cn.thinglin.nio;
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.SocketChannel;
import java.util.Iterator;
public class NIOClient implements Runnable{
public Selector mSelector;
public static final int PORT = 18881;
public NIOClient(int port){
SocketChannel sc = null;
try {
sc = SocketChannel.open();
sc.configureBlocking(false);
mSelector = Selector.open();
sc.connect(new InetSocketAddress("127.0.0.1", port));
sc.register(mSelector,SelectionKey.OP_CONNECT); //监听连接完成
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean connect(SelectionKey key){
SocketChannel sc = (SocketChannel) key
.channel();
try {
if(sc.isConnectionPending()){
sc.finishConnect();
}
sc.configureBlocking(false);
ByteBuffer writeBuf = ByteBuffer.allocate(1024);
sc.write(writeBuf.wrap(new String("无聊啊").getBytes()));
sc.register(mSelector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
private boolean read(SelectionKey key){
SocketChannel sc = (SocketChannel) key.channel();
try {
ByteBuffer readBuf = ByteBuffer.allocate(1024);
sc.read(readBuf);
String msg = new String(readBuf.array(),"UTF-8").trim();
System.out.println("服务端回信:"+msg);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
@Override
public void run() {
while(true){
try {
mSelector.select();
Iterator<SelectionKey> iterator = mSelector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey next = iterator.next();
iterator.remove();
//连接完成
if(next.isConnectable()){
connect(next);
}
//读
if(next.isReadable()){
read(next);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread(new NIOClient(PORT)).start();
}
}
