package com.fuchanghai.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 MyNIOClient {
private Selector selector=null;
public void initClient (String ip , int port) throws IOException{
//相当于bio中的serversocket
ServerSocketChannel serverChannel= ServerSocketChannel.open();
//设置非阻塞
serverChannel.configureBlocking(false);
//绑定到对应的端口
serverChannel.socket().bind(new InetSocketAddress(ip, port));
this.selector=Selector.open();
//将通道选择器和通道绑定,一有时间连接就注册成accept
serverChannel.register(selector,SelectionKey.OP_ACCEPT);
}
public void listen(){
//轮询事件管理器(多路复用器)
while(true){
try {
//阻塞处,可设置时间使其不阻塞
this.selector.select();
//此处设置时间
//获取所有的注册的事件
Iterator<?> iteratorKey =this.selector.selectedKeys().iterator();
//取出注册的事件,并交给handle方法处理
while (iteratorKey.hasNext()){
SelectionKey selectionKey=(SelectionKey) iteratorKey.next();
iteratorKey.remove();
handle(selectionKey);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void handle(SelectionKey selectionKey) throws IOException{
if(selectionKey.isAcceptable()){
//如果是新接入进来的 那么将ServerSockteChannel 得到,并且将其注册到selector中
ServerSocketChannel serverSocketChannel =(ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel =serverSocketChannel.accept();
socketChannel.configureBlocking(false);
//给通道设置读的权限。这样服务器才能经过通道读到客户端发来的信息
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (selectionKey.isReadable()) {
SocketChannel socketChannel =(SocketChannel) selectionKey.channel();
ByteBuffer bf = ByteBuffer.allocate(1024);
int data=socketChannel.read(bf);
if(data!=-1){
String msg =new String(bf.array(),"GBK");
}
}
}
}
下面是我在听课期间模拟老师讲的实例化的流程图
首先client1到4 就是客户端发起的链接, 虚线部分实际上在代码中没有实现。实线为实现了的。 传统bio 一个client对应的服务端就需要创建一个线程。如诺无线创建线程,那么机器碰到大量的请求总会爆掉。如诺设置个线程池限制线程数量防止线程过多。实际上高并发的时候是不够用的。
使用了nio我们会将各种读写时间,和链接事件都交给selector管理。绑定类型(例如SelectionKey.OP_ACCEPT,注accpet是请求接收类型,connect是已接收类型)使得listen()知道client 要进行哪种操作。这4个顾客发起任何类型请求都会注册到selector中listen()中 ,死循环取得多路复用器中的事件(selector.select())就相当于一个服务员给多个顾客服务,即取得顾客放入selector中的请求。 我们在上面的代码中设置时间(有个注解叫设置时间的),使得程序非阻塞。实际上如果顾客1的点餐请求花费时间太长的话,还是会阻塞其他顾客的请求的。这只是初步了解。之后还要更新