1 packageniochat;2
3 importjava.io.IOException;4 importjava.net.InetSocketAddress;5 importjava.nio.ByteBuffer;6 importjava.nio.channels.Channel;7 importjava.nio.channels.SelectionKey;8 importjava.nio.channels.Selector;9 importjava.nio.channels.ServerSocketChannel;10 importjava.nio.channels.SocketChannel;11 importjava.nio.charset.Charset;12
13 public classNServer {14 //用于检查所有channel状态的selector
15 private Selector selector = null;16 static final int PORT = 3001;17 private Charset charset = Charset.forName("utf-8");18 public void init() throwsIOException, InterruptedException {19 selector =Selector.open();20 //通过open方法打开一个未绑定的ServerSocketChannel实例
21 ServerSocketChannel server =ServerSocketChannel.open();22 InetSocketAddress isa = new InetSocketAddress("127.0.0.1", PORT);23 server.bind(isa);24 //设置非阻塞
25 server.configureBlocking(false);26 //注册ServerSocketChannel到selector
27 server.register(selector, SelectionKey.OP_ACCEPT);28
29 //服务器端需要轮询selector看是否有channel需要通信
30 while ( true) {31 //select()将会一直阻塞,直到选择了至少一个channel(进行通信),此时sector就会调用wakeup(),select()方法才能返回
32 int num =selector.select();33 Thread.sleep(1000);34 //select()方法返回后,用selectedKeys()返回对应channel的SelectionKey集合,通过key.channel()方法可以返回对应的channel实例35 //被选中的key集合selectedKeys表示需要进行IO处理的channel集合,一个key代表一个channel
36 System.out.println("SelectionKey.size = "+selector.selectedKeys().size());37 for(SelectionKey sk : selector.selectedKeys()) {38 //从selector上的已选择的key集合中删除正在处理的key
39 System.out.println("SelectionKey.value = "+sk);40 selector.selectedKeys().remove(sk);41 //如果sk对应channel包含客户端连接请求
42 if(sk.isAcceptable()) {43 //调用accept方法接受请求,产生一个服务器端的SocketChannel44 //在非阻塞模式下,如果没有连接则直接返回null
45 SocketChannel sc =server.accept();46 //设置非阻塞模式
47 sc.configureBlocking(false);48 //将SocketChannel也注册到selector
49 sc.register(selector, SelectionKey.OP_READ);50 //再将sk对应的channel设置为请求准备接受其他请求
51 sk.interestOps(SelectionKey.OP_ACCEPT);52 }53
54 //如果sk对应的channel有数据需要读取
55 if(sk.isReadable()) {56 //获取sk对应的channel
57 SocketChannel sc =(SocketChannel)sk.channel();58 //channel中的数据必须先写入buffer中,然后才能写入进content中
59 ByteBuffer buff = ByteBuffer.allocate(1024);60 String content = "";61 try{62 while(sc.read(buff) > 0) {63 //buffer 复位
64 buff.flip();65 content +=charset.decode(buff);66 }67 System.out.println("读取的数据:"+content);68 //sk复位
69 sk.interestOps(SelectionKey.OP_READ);70 } //遇到channel有异常说明客户端有异常,取消注册此sk
71 catch(IOException ex) {72 //从已选择key集合中取消sk,下一次select()时此channel将自动被删除
73 ex.printStackTrace();74
75 sk.cancel();76 if(sk.channel() != null) {77 sk.channel().close();78 }79
80 }81
82 if (content.length() > 0) {83 //广播
84 for(SelectionKey key : selector.keys()) {85 Channel targetChannel =key.channel();86 if (targetChannel instanceofSocketChannel) {87 SocketChannel dest =(SocketChannel)targetChannel;88 dest.write(charset.encode(content));89 }90 }91 }92 }93
94 }95 }96 }97
98 public static void main(String[] args) throwsIOException, InterruptedException {99 newNServer().init();100 }101 }