package com.example.nettytestall.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) throws IOException {
//创建serverSocketChannel ->ServerSocket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//绑定一个端口8888,在服务器端监听
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
//得到一个Selector对象
Selector selector = Selector.open();
//把serverSocketChannel 注册到selector 关心事件为 OP_ACCEPT
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
//这里我们等待1秒 如果没有事件发生,返回
if(selector.select(1000)==0){//没有事件发生
/*System.out.println("服务器等待1秒无连接");*/
}
//如果返回大于0 就获取到相关的selectionkey集合
//1.如果返回的>0,标识一级获取到关注的事件
//2.selector.selectedKeys() 返回关注事件的集合
// 通过selectionKeys 反向获取通道
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历set 使用迭代器遍历(遍历出每一个selectionKey)
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
//获取到selectorKet 判断通道里面发生的事件做相应的处理
SelectionKey key = iterator.next();
if(key.isAcceptable()){ //如果是OP_ACCEPT,有新客户端连接
//该客户端生成一个SocketChannel
// (accept 是阻塞方法 此设计巧妙在先确定有客户端连接 在调用此方法 Bio是先调用,等待客户端连接 )
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("客户端连接成功");
//非阻塞
socketChannel.configureBlocking(false);
//将socketChannel 注册到selector ,关注事件为OP_READ 同时给通道绑定buffer
socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
if(key.isReadable()){//发生读的事件
//通过key 反向获取到对应的channel
SocketChannel channel = (SocketChannel)key.channel();
//获取到关联的buffer
ByteBuffer buffer = (ByteBuffer)key.attachment();
//这个反回值其实就是读取的字节数。该数字为0时说明就是一般的没有数据可读取,而当为-1时其实表示底层tcp已经断开了。
// (但IE的连接有点不同,read时直接给出Exception,反正这些情况都要判断了.)
//不做这些处理 如果就读 会出现客户端关闭后,服务端死循环读取
try{
int num = channel.read(buffer);
if(num ==-1){
throw new IOException("读完成");
}else {
System.out.println("客户端:"+new String(buffer.array()));
}
}catch (IOException e){
key.cancel();//取消绑定
if(channel!=null){
channel.close();
}
}
}
//手动从集合中删除当前的selectionKey 防止重复操作
selectionKeys.remove(key);
}
}
}
}