服务器端:

1.  import java.io.IOException;
2.  import java.net.InetSocketAddress;
3.  import java.nio.ByteBuffer;
4.  import java.nio.channels.SelectableChannel;
5.  import java.nio.channels.SelectionKey;
6.  import java.nio.channels.Selector;
7.  import java.nio.channels.ServerSocketChannel;
8.  import java.nio.channels.SocketChannel;
9.  import java.nio.charset.Charset;
10.  
11.  public class ServerNio {
12.          public static void main(String[] args) throws IOException{
13.                  //打开一个多路复用器对象,作用是监控多个SelectableChannel的IO状况。
14.                  Selector selector = Selector.open();
15.                  //使用open()方法打开一个未绑定的服务器插口通道,支持非阻塞操作
16.                  ServerSocketChannel serverChannel = ServerSocketChannel.open();
17.                  //将服务器插口绑定到指定ip和端口号
18.                  serverChannel.socket().bind( new InetSocketAddress("127.0.0.1",55555));
19.                  //调整服务器插口通道的阻塞模式为非阻塞模式
20.                  serverChannel.configureBlocking(false);
21.                  //将服务器插口通道注册到多路复用器(selector选择容器中)
22.                  //SelectionKey表示注册通道时产生的选择键
23.                  serverChannel.register(selector , SelectionKey.OP_ACCEPT);
24.                  
25.                  //创建一个1字节的缓冲区
26.                  ByteBuffer buffer = ByteBuffer.allocate(1024);
27.                  //指定编码集
28.                  Charset charset = Charset.forName("UTF-8");
29.                  
30.                  //服务器不断地调用select()方法,若大于0表示有多少个Channel通道具有可用的I/O操作
31.                  while(selector.select() > 0){
32.                          //遍历已经注册到selector容器的通道
33.                          for(SelectionKey sk: selector.selectedKeys()){
34.                                  //把正在处理的sk从集合中移除
35.                                  selector.selectedKeys().remove(sk);        
36.                                  
37.                                  //如果sk对应的通道包含客户端的连接请求
38.                                  if(sk.isAcceptable()){
39.                                          //返回创建此键的通道
40.                                          SelectableChannel sc1 = sk.channel();
41.                                          //如果这个通道是服务器插口通道类型的
42.                                          if(sc1 instanceof ServerSocketChannel){
43.                                                  //将其强制转换成服务器插口通道类型的对象
44.                                                  ServerSocketChannel ssc = (ServerSocketChannel)sc1;
45.                                                  //调用accept()方法接受连接并返回普通插口通道类型的I/O接口
46.                                                  SocketChannel socketChannel1 = ssc.accept();
47.                                                  //设置采用非阻塞模式
48.                                                  socketChannel1.configureBlocking(false);
49.                                                  //将插口通道也注册到selector
50.                                                  socketChannel1.register(selector,SelectionKey.OP_READ);
51.                                                  //将sk对应的Channel设置成准备接受其他请求
52.                                                  sk.interestOps(SelectionKey.OP_ACCEPT);
53.                                          }
54.                                  }
55.                                  
56.                                  //如果sk对应的通道有数据需要读取
57.                                  if(sk.isReadable()){
58.                                          //获取该SelectionKey对应的Channel,该Channel中有可读得数据
59.                                          SelectableChannel sc2 = sk.channel();
60.                                          if(sc2 instanceof SocketChannel){
61.                                                  SocketChannel socketChannel2 = (SocketChannel)sc2;
62.                                                  String msg = "";
63.                                                  
64.                                                  //开始读取数据
65.                                                  while(socketChannel2.read(buffer)>0){
66.                                                          buffer.flip();        //重绕(锁定)缓冲区(pos=0,limit=pos)
67.                                                          msg+=charset.decode(buffer);
68.                                                          System.out.println(msg);
69.                                                          buffer.clear();        //重置缓冲区(pos=0,limit=capacity)
70.                                                  }
71.                                                  //将sk对应的Channel设置成准备下一次读取
72.                                                  sk.interestOps(SelectionKey.OP_READ);
73.                                                  
74.                                                  //发送给客户端
75.                                                  //如果msg的长度大于0,即聊天信息不为空
76.                                                  if(msg.length()>0){
77.                                                          //遍历该selector里注册的所有SelectKey
78.                                                          for(SelectionKey key:selector.keys()){
79.                                                                  //获取该key对应的Channel
80.                                                                  SelectableChannel targetChannel = key.channel();
81.                                                                  if(targetChannel instanceof SocketChannel){
82.                                                                          //将读到的内容写入该Channel中
83.                                                                          SocketChannel dest =(SocketChannel)targetChannel;
84.                                                                          dest.write(charset.encode(msg));
85.                                                                  }
86.                                                          }
87.                                                  }
88.                                          }
89.                                  }
90.                          }
91.                  }
92.          }
93.  }


复制代码

客户端:

1.  import java.io.IOException;
2.  import java.net.InetSocketAddress;
3.  import java.nio.channels.SelectionKey;
4.  import java.nio.channels.Selector;
5.  import java.nio.channels.SocketChannel;
6.  import java.nio.charset.Charset;
7.  import java.util.Scanner;
8.  
9.  
10.  public class ClientNio {
11.          public static void main(String[] args) throws IOException{
12.                  //定义检测SocketChannel的Selector对象
13.                  Selector selector = Selector.open();
14.                  InetSocketAddress server = new InetSocketAddress("127.0.0.1",55555);
15.                  //调用open静态方法创建连接到指定主机的SocketChannel
16.                  SocketChannel clientChannel = SocketChannel.open(server);
17.                  //设置该客户端插口通道为非阻塞模式
18.                  clientChannel.configureBlocking(false);
19.                  //注册插口到selector容器
20.                  clientChannel.register(selector, SelectionKey.OP_READ);
21.                  
22.                  //定义编码和解码的字符集
23.                  Charset charset = Charset.forName("UTF-8");
24.                  
25.                  //启动一个线程用于读取服务器发送来的数据
26.                  new ClientNioThread(selector,charset);
27.                  
28.                  //创建键盘输入流
29.                  Scanner scanner = new Scanner(System.in);
30.                  
31.                  //向服务器发送数据
32.                  while( scanner.hasNextLine()){
33.                          //读取键盘输入
34.                          String message = scanner.nextLine();
35.                          //将键盘输入的内容输出到SocketChannel
36.                          clientChannel.write(charset.encode(message));
37.                  }
38.          }
39.  }


复制代码

读取服务器端数据的线程:

1.  import java.io.IOException;
2.  import java.nio.ByteBuffer;
3.  import java.nio.channels.SelectableChannel;
4.  import java.nio.channels.SelectionKey;
5.  import java.nio.channels.Selector;
6.  import java.nio.channels.SocketChannel;
7.  import java.nio.charset.Charset;
8.  
9.  public class ClientNioThread extends Thread{
10.          private Selector selector;
11.          private Charset charset;
12.          
13.          public ClientNioThread(Selector selector,Charset charset){
14.                  this.selector = selector;
15.                  this.charset = charset;
16.                  this.start();        //启动线程
17.          }
18.  
19.          @Override
20.          public void run() {
21.                  try{
22.                          ByteBuffer buffer = ByteBuffer.allocate(1024);
23.                          while( selector.select() > 0 ){
24.                                  //遍历每个有可用I/O操作Channel对应的SelectionKey
25.                                  for( SelectionKey sk : selector.selectedKeys() ){
26.                                          //删除正在处理的SelectionKey
27.                                          selector.selectedKeys().remove(sk);
28.                                          
29.                                          SelectableChannel sc = sk.channel();
30.                                          if( sc instanceof SocketChannel ){
31.                                                  SocketChannel socketChannel = (SocketChannel) sc;
32.                                                  StringBuilder sb = new StringBuilder();
33.                                                  while( socketChannel.read( buffer ) > 0){
34.                                                          buffer.flip();
35.                                                          sb.append( charset.decode( buffer ));
36.                                                          buffer.clear();
37.                                                  }
38.                                                  //打印输出读取的内容
39.                                                  System.out.println( sb.toString());
40.                                          }
41.                                          //为下一次读取做准备
42.                                          sk.interestOps( SelectionKey.OP_READ);
43.                                  }
44.                          }
45.                  }catch(IOException e){
46.                          e.printStackTrace();
47.                  }
48.                  
49.          }
50.          
51.  }


复制代码

总结:


服务器端:

1-获得Selector对象

2 获得ServerSocketChannel 对象serverChannel

3 把serverChannel跟指定的ip和端口(serverChannel对应的ServerSocket)

4 设置非阻塞模式

5 把serverChannel 注册到selector,指定模式(SelectionKey.OP_ACCEPT)

6 反复执行

        1> select()选择可以选择的通道

        2> 遍历所有的selectedKeys

           isAcceptable:打开监听 SocketChannel channel = serverChannel.accept();

                        把通过监听获得到的channel设置为无阻塞模式

                        注册channel到selector上,同时指定模式(OP_READ)

                        为sk的interest集合插入一个值(为下次操作做准备)

                        sk.interestOps(SelectionKey)

          sk.isReadable:根据sk获得它对应的通道:SelectableChannel sc = sk.channel();

                        类型检查if(sc.instanceof SocketChannel)

                        如果满足条件做类型转换:SocketChannel socketChannel = (SocketChannel)sc

                        读取数据(读取ByteBuffer)socketChannel.read(buffer);

                        向客户端发送:

if(msg.length()>0){
                                 //遍历keys
                                 for(SelectionKey key:selector keys()){
                                         SelectableChannel selectChannel = key.channel();
                                         if(selectChannel instance of SocketChannel){
                                                 SocketChannel sssccc = (SocketChannel)selectChannel
                                                 sssccc.write(charset.encode(msg));
                                         }
                                 }
                         }

客户端:

1 获得一个Selector对象selector;

2 获得SocketChannel同时指定要连接的服务器的InetSocketAddress对象

3 设置为非阻塞模式

4 注册SocketChannel到selector同时指定模式为SelectionKey.OP_READ

5 使用主线程向服务器发送数据

6 使用单独的一个线程读取服务器发送来的数据