服务器端:
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 使用单独的一个线程读取服务器发送来的数据