一.非阻塞
1.非阻塞:线程执行方法时,如果操作没有就绪,就立即返回,不会一直等待操作就绪
2.java.nio提供非阻塞通信的类:
1)ServerSocketChannel:代替Server
2)SocketChannel:代替Socket
3)Selector:监控就绪事件
4)SelectionKey:注册事件的句柄
3.服务器端程序使用多线程处理阻塞IO,虽然可以响应多用户,但是存在局限性
1)JVM会给每个线程分配独立的资源,线程越多,系统开销越大
2)容易造成死锁
3)需要频繁转让CPU使用
4.完成多个任务可以使用多线程分别处理,也可以让单个线程以轮询的工作方式处理就绪的任务
二.创建非阻塞服务器端程序
对于服务区端造成阻塞的原因有:
1)客户端的连接
2)接收客户端的数据
3)响应客户端的数据
所以,可以在主线程中分别监控这些任务,当有任何一个就绪就执行
public class nio_demo2 {
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private int port = 6666;
private Charset charset = Charset.forName("UTF-8");
public nio_demo2() throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("server start...");
}
public void service() throws IOException {
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//select():监控注册的channel,当有需要处理的IO时,将对应的selectionKey加入到selected-key集合
while(selector.select()>0){
Set readyKey = selector.selectedKeys();
Iterator it = readyKey.iterator();
while(it.hasNext()){
SelectionKey key = null;
try{
key = (SelectionKey)it.next();
it.remove();
if(key.isAcceptable()){
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel socketChannel = ssc.accept();
System.out.println("client connection" +
socketChannel.socket().getInetAddress()+":"
+socketChannel.socket().getPort());
//设置为非阻塞
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
//向selecctor注册读就绪事件和写就绪时件,同时关联buffer
socketChannel.register(selector,
SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);
}
if(key.isReadable()){
receive(key);
}
if(key.isWritable()){
send(key);
}
}catch (IOException e){
e.printStackTrace();
try{
if(key!=null){
key.cancel();
key.channel().close();
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
}
//发送,处理写就绪事件
public void send(SelectionKey key) throws IOException {
//获得关联的附件
ByteBuffer buffer = (ByteBuffer)key.attachment();
SocketChannel socketChannel = (SocketChannel)key.channel();
buffer.flip();
String data = decode(buffer);
if(data.indexOf("\n")==-1){
return;
}
String outputData = data.substring(0,data.indexOf("\n")+1);
System.out.println(outputData);
ByteBuffer outputBuffer = encode("echo:"+outputData);
//发送一行数据
while(outputBuffer.hasRemaining()){
socketChannel.write(outputBuffer);
}
//删除已处理数据(读写操作同一个ByteBuffer)
ByteBuffer temp = encode(outputData);
buffer.position(temp.limit());
buffer.compact();
if(outputData.equals("end\n")){
key.cancel();
socketChannel.close();
System.out.println("close coonnection");
}
}
//接收,处理读就绪事件
public void receive(SelectionKey key) throws IOException {
//获取关联的buffer
ByteBuffer buffer = (ByteBuffer)key.attachment();
//获取关联的channel
SocketChannel socketChannel = (SocketChannel)key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(32);
socketChannel.read(readBuffer);
//flip:将极限设置为位置,将位置设置为0
readBuffer.flip();
buffer.limit(buffer.capacity());
buffer.put(readBuffer);
}
//解码
public String decode(ByteBuffer buffer){
CharBuffer charBuffer = charset.decode(buffer);
return charBuffer.toString();
}
//编码
public ByteBuffer encode(String str){
return charset.encode(str);
}
public static void main(String[] args) throws IOException {
nio_demo2 server = new nio_demo2();
server.service();
}
}