标签:
最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO。下面是我结合网络资料自己总结的,为了节约时间图示随便画的,能达意就行。
简介:
BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。虽然此时服务器具备了高并发能力,即能够同时处理多个客户端请求了,但是却带来了一个问题,随着开启的线程数目增多,将会消耗过多的内存资源,导致服务器变慢甚至崩溃,NIO可以一定程度解决这个问题。
到这里你也许已经发现,一旦有请求到来(不管是几个同时到还是只有一个到),都会调用对应IO处理函数处理,所以:
(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。
(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
1. package cn.nio; 2. 3. import java.io.IOException; 4. import java.net.InetSocketAddress; 5. import java.nio.ByteBuffer; 6. import java.nio.channels.SelectionKey; 7. import java.nio.channels.Selector; 8. import java.nio.channels.ServerSocketChannel; 9. import java.nio.channels.SocketChannel; 10. import java.util.Iterator; 11. 12. /** 13. * NIO服务端 14. * @author 小路 15. */ 16. public class NIOServer { 17. //通道管理器 18. private Selector selector; 19. 20. /** 21. * 获得一个ServerSocket通道,并对该通道做一些初始化的工作 22. * @param port 绑定的端口号 23. * @throws IOException 24. */ 25. public void initServer(int port) throws IOException { 26. // 获得一个ServerSocket通道 27. ServerSocketChannel serverChannel = ServerSocketChannel.open(); 28. // 设置通道为非阻塞 29. serverChannel.configureBlocking(false); 30. // 将该通道对应的ServerSocket绑定到port端口 31. serverChannel.socket().bind(new InetSocketAddress(port)); 32. // 获得一个通道管理器 33. this.selector = Selector.open(); 34. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, 35. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 36. serverChannel.register(selector, SelectionKey.OP_ACCEPT); 37. } 38. 39. /** 40. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 41. * @throws IOException 42. */ 43. @SuppressWarnings("unchecked") 44. public void listen() throws IOException { 45. System.out.println("服务端启动成功!"); 46. // 轮询访问selector 47. while (true) { 48. //当注册的事件到达时,方法返回;否则,该方法会一直阻塞 49. selector.select(); 50. // 获得selector中选中的项的迭代器,选中的项为注册的事件 51. Iterator ite = this.selector.selectedKeys().iterator(); 52. while (ite.hasNext()) { 53. SelectionKey key = (SelectionKey) ite.next(); 54. // 删除已选的key,以防重复处理 55. ite.remove(); 56. // 客户端请求连接事件 57. if (key.isAcceptable()) { 58. ServerSocketChannel server = (ServerSocketChannel) key 59. .channel(); 60. // 获得和客户端连接的通道 61. SocketChannel channel = server.accept(); 62. // 设置成非阻塞 63. channel.configureBlocking(false); 64. 65. //在这里可以给客户端发送信息哦 66. channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes())); 67. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 68. channel.register(this.selector, SelectionKey.OP_READ); 69. 70. // 获得了可读的事件 71. } else if (key.isReadable()) { 72. read(key); 73. } 74. 75. } 76. 77. } 78. } 79. /** 80. * 处理读取客户端发来的信息 的事件 81. * @param key 82. * @throws IOException 83. */ 84. public void read(SelectionKey key) throws IOException{ 85. // 服务器可读取消息:得到事件发生的Socket通道 86. SocketChannel channel = (SocketChannel) key.channel(); 87. // 创建读取的缓冲区 88. ByteBuffer buffer = ByteBuffer.allocate(10); 89. channel.read(buffer); 90. byte[] data = buffer.array(); 91. String msg = new String(data).trim(); 92. System.out.println("服务端收到信息:"+msg); 93. ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); 94. channel.write(outBuffer);// 将消息回送给客户端 95. } 96. 97. /** 98. * 启动服务端测试 99. * @throws IOException 100. */ 101. public static void main(String[] args) throws IOException { 102. NIOServer server = new NIOServer(); 103. server.initServer(8000); 104. server.listen(); 105. } 106. 107. }
1. package cn.nio; 2. 3. import java.io.IOException; 4. import java.net.InetSocketAddress; 5. import java.nio.ByteBuffer; 6. import java.nio.channels.SelectionKey; 7. import java.nio.channels.Selector; 8. import java.nio.channels.SocketChannel; 9. import java.util.Iterator; 10. 11. /** 12. * NIO客户端 13. * @author 小路 14. */ 15. public class NIOClient { 16. //通道管理器 17. private Selector selector; 18. 19. /** 20. * 获得一个Socket通道,并对该通道做一些初始化的工作 21. * @param ip 连接的服务器的ip 22. * @param port 连接的服务器的端口号 23. * @throws IOException 24. */ 25. public void initClient(String ip,int port) throws IOException { 26. // 获得一个Socket通道 27. SocketChannel channel = SocketChannel.open(); 28. // 设置通道为非阻塞 29. channel.configureBlocking(false); 30. // 获得一个通道管理器 31. this.selector = Selector.open(); 32. 33. // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 34. //用channel.finishConnect();才能完成连接 35. channel.connect(new InetSocketAddress(ip,port)); 36. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 37. channel.register(selector, SelectionKey.OP_CONNECT); 38. } 39. 40. /** 41. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 42. * @throws IOException 43. */ 44. @SuppressWarnings("unchecked") 45. public void listen() throws IOException { 46. // 轮询访问selector 47. while (true) { 48. selector.select(); 49. // 获得selector中选中的项的迭代器 50. Iterator ite = this.selector.selectedKeys().iterator(); 51. while (ite.hasNext()) { 52. SelectionKey key = (SelectionKey) ite.next(); 53. // 删除已选的key,以防重复处理 54. ite.remove(); 55. // 连接事件发生 56. if (key.isConnectable()) { 57. SocketChannel channel = (SocketChannel) key 58. .channel(); 59. // 如果正在连接,则完成连接 60. if(channel.isConnectionPending()){ 61. channel.finishConnect(); 62. 63. } 64. // 设置成非阻塞 65. channel.configureBlocking(false); 66. 67. //在这里可以给服务端发送信息哦 68. channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes())); 69. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 70. channel.register(this.selector, SelectionKey.OP_READ); 71. 72. // 获得了可读的事件 73. } else if (key.isReadable()) { 74. read(key); 75. } 76. 77. } 78. 79. } 80. } 81. /** 82. * 处理读取服务端发来的信息 的事件 83. * @param key 84. * @throws IOException 85. */ 86. public void read(SelectionKey key) throws IOException{ 87. //和服务端的read方法一样 88. } 89. 90. 91. /** 92. * 启动客户端测试 93. * @throws IOException 94. */ 95. public static void main(String[] args) throws IOException { 96. NIOClient client = new NIOClient(); 97. client.initClient("localhost",8000); 98. client.listen(); 99. } 100. 101. }
标签:
原文地址:http://blog.csdn.net/jiyiqinlovexx/article/details/42619097