您好,欢迎来到榕意旅游网。
搜索
您的当前位置:首页关于BIO和NIO的理解

关于BIO和NIO的理解

来源:榕意旅游网
关于BIO和NIO的理解

摘要: 关于BIO和NIO的理解

最近⼤概看了ZooKeeper和Mina的源码发现都是⽤Java NIO实现的,所以有必要搞清楚什么是NIO。下⾯是我结合⽹络资料⾃⼰总结的,为了节约时间图⽰随便画的,能达意就⾏。 简介:

BIO:同步阻塞式IO,服务器实现模式为⼀个连接⼀个线程,即客户端有连接请求时服务器端就需要启动⼀个线程进⾏处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO:同步⾮阻塞式IO,服务器实现模式为⼀个请求⼀个线程,即客户端发送的连接请求都会注册到多路复⽤器上,多路复⽤器轮询到连接有I/O请求时才启动⼀个线程进⾏处理。

AIO(NIO.2):异步⾮阻塞式IO,服务器实现模式为⼀个有效请求⼀个线程,客户端的I/O请求都是由OS先完成了再通知服务器应⽤去启动线程进⾏处理。

BIO

同步阻塞式IO,相信每⼀个学习过操作系统⽹络编程或者任何语⾔的⽹络编程的⼈都很熟悉,在while循环中服务端会调⽤accept⽅法等待接收客户端的连接请求,⼀旦接收到⼀个连接请求,就可以建⽴通信套接字在这个通信套接字上进⾏读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执⾏完成。

如果BIO要能够同时处理多个客户端请求,就必须使⽤多线程,即每次accept阻塞等待来⾃客户端请求,⼀旦受到连接请求就建⽴通信套接字同时开启⼀个新的线程来处理这个套接字的数据读写请求,然后⽴刻⼜继续accept等待其他客户端连接请求,即为每⼀个客户端连接请求都创建⼀个线程来单独处理,⼤概原理图就像这样:

虽然此时服务器具备了⾼并发能⼒,即能够同时处理多个客户端请求了,但是却带来了⼀个问题,随着开启的线程数⽬增多,将会消耗过多的内存资源,导致服务器变慢甚⾄崩溃,NIO可以⼀定程度解决这个问题。

NIO

同步⾮阻塞式IO,关键是采⽤了事件驱动的思想来实现了⼀个多路转换器。

NIO与BIO最⼤的区别就是只需要开启⼀个线程就可以处理来⾃多个客户端的IO事件,这是怎么做到的呢? 就是多路复⽤器,可以监听来⾃多个客户端的IO事件:

A. 若服务端监听到客户端连接请求,便为其建⽴通信套接字(java中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建⽴通信套接字。

B. 若服务端监听到来⾃已经创建了通信套接字的客户端发送来的数据,就会调⽤对应接⼝处理接收到的数据,若同时有多个客户端发来数据也可以依次进⾏处理。

C. 监听多个客户端的连接请求和接收数据请求同时还能监听⾃⼰时候有数据要发送。

总之就是在⼀个线程中就可以调⽤多路复⽤接⼝(java中是select)阻塞同时监听来⾃多个客户端的IO请求,⼀旦有收到IO请求就调⽤对应函数处理。 各⾃应⽤场景

到这⾥你也许已经发现,⼀旦有请求到来(不管是⼏个同时到还是只有⼀个到),都会调⽤对应IO处理函数处理,所以:(1)NIO适合处理连接数⽬特别多,但是连接⽐较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。(2)BIO⽅式适⽤于连接数⽬⽐较⼩且固定的场景,这种⽅式对服务器资源要求⽐较⾼,并发局限于应⽤中。

附录:下⾯附上⼀个别⼈写的java NIO的例⼦。 服务端:

1. 1. package cn.nio; 2. 2.

3. 3. import java.io.IOException;

4. 4. import java.net.InetSocketAddress; 5. 5. import java.nio.ByteBuffer;

6. 6. import java.nio.channels.SelectionKey; 7. 7. import java.nio.channels.Selector;

8. 8. import java.nio.channels.ServerSocketChannel; 9. 9. import java.nio.channels.SocketChannel; 10. 10. import java.util.Iterator; 11. 11. 12. 12. /**

13. 13. * NIO服务端 14. 14. * 15. 15. */

16. 16. public class NIOServer { 17. 17. //通道管理器

18. 18. private Selector selector; 19. 19. 20. 20. /**

21. 21. * 获得⼀个ServerSocket通道,并对该通道做⼀些初始化的⼯作 22. 22. * @param port 绑定的端⼝号 23. 23. * @throws IOException 24. 24. */

25. 25. public void initServer(int port) throws IOException { 26. 26. // 获得⼀个ServerSocket通道

27. 27. ServerSocketChannel serverChannel = ServerSocketChannel.open(); 28. 28. // 设置通道为⾮阻塞

29. 29. serverChannel.configureBlocking(false);

30. 30. // 将该通道对应的ServerSocket绑定到port端⼝

31. 31. serverChannel.socket().bind(new InetSocketAddress(port)); 32. 32. // 获得⼀个通道管理器

33. 33. this.selector = Selector.open();

34. 34. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, 35. 35. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会⼀直阻塞。 36. 36. serverChannel.register(selector, SelectionKey.OP_ACCEPT); 37. 37. } 38. 38. 39. 39. /**

40. 40. * 采⽤轮询的⽅式监听selector上是否有需要处理的事件,如果有,则进⾏处理 41. 41. * @throws IOException 42. 42. */

43. 43. @SuppressWarnings(\"unchecked\") 44. 44. public void listen() throws IOException { 45. 45. System.out.println(\"服务端启动成功!\"); 46. 46. // 轮询访问selector 47. 47. while (true) {

48. 48. //当注册的事件到达时,⽅法返回;否则,该⽅法会⼀直阻塞 49. 49. selector.select();

50. 50. // 获得selector中选中的项的迭代器,选中的项为注册的事件 51. 51. Iterator ite = this.selector.selectedKeys().iterator(); 52. 52. while (ite.hasNext()) {

53. 53. SelectionKey key = (SelectionKey) ite.next(); 54. 54. // 删除已选的key,以防重复处理 55. 55. ite.remove();

56. 56. // 客户端请求连接事件 57. 57. if (key.isAcceptable()) {

58. 58. ServerSocketChannel server = (ServerSocketChannel) key 59. 59. .channel();

60. 60. // 获得和客户端连接的通道

61. 61. SocketChannel channel = server.accept(); 62. 62. // 设置成⾮阻塞

63. 63. channel.configureBlocking(false); 64. 64.

65. 65. //在这⾥可以给客户端发送信息哦

66. 66. channel.write(ByteBuffer.wrap(new String(\"向客户端发送了⼀条信息\").getBytes()));

67. 67. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 68. 68. channel.register(this.selector, SelectionKey.OP_READ); 69. 69.

70. 70. // 获得了可读的事件

71. 71. } else if (key.isReadable()) { 72. 72. read(key); 73. 73. } 74. 74.

75. 75. } 76. 76.

77. 77. } 78. 78. } 79. 79. /**

80. 80. * 处理读取客户端发来的信息 的事件

81. 81. * @param key

82. 82. * @throws IOException 83. 83. */

84. 84. public void read(SelectionKey key) throws IOException{ 85. 85. // 服务器可读取消息:得到事件发⽣的Socket通道

86. 86. SocketChannel channel = (SocketChannel) key.channel(); 87. 87. // 创建读取的缓冲区

88. 88. ByteBuffer buffer = ByteBuffer.allocate(10); 89. 89. channel.read(buffer);

90. 90. byte[] data = buffer.array();

91. 91. String msg = new String(data).trim();

92. 92. System.out.println(\"服务端收到信息:\"+msg);

93. 93. ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); 94. 94. channel.write(outBuffer);// 将消息回送给客户端 95. 95. } 96. 96. 97. 97. /**

98. 98. * 启动服务端测试

99. 99. * @throws IOException 100. 100. */

101. 101. public static void main(String[] args) throws IOException { 102. 102. NIOServer server = new NIOServer(); 103. 103. server.initServer(8000); 104. 104. server.listen(); 105. 105. } 106. 106. 107. 107. }

客户端:

1. 1. package cn.nio; 2. 2.

3. 3. import java.io.IOException;

4. 4. import java.net.InetSocketAddress; 5. 5. import java.nio.ByteBuffer;

6. 6. import java.nio.channels.SelectionKey; 7. 7. import java.nio.channels.Selector;

8. 8. import java.nio.channels.SocketChannel; 9. 9. import java.util.Iterator; 10. 10. 11. 11. /**

12. 12. * NIO客户端 13. 13. * 14. 14. */

15. 15. public class NIOClient { 16. 16. //通道管理器

17. 17. private Selector selector; 18. 18. 19. 19. /**

20. 20. * 获得⼀个Socket通道,并对该通道做⼀些初始化的⼯作 21. 21. * @param ip 连接的服务器的ip

22. 22. * @param port 连接的服务器的端⼝号 23. 23. * @throws IOException 24. 24. */

25. 25. public void initClient(String ip,int port) throws IOException { 26. 26. // 获得⼀个Socket通道

27. 27. SocketChannel channel = SocketChannel.open(); 28. 28. // 设置通道为⾮阻塞

29. 29. channel.configureBlocking(false); 30. 30. // 获得⼀个通道管理器

31. 31. this.selector = Selector.open(); 32. 32.

33. 33. // 客户端连接服务器,其实⽅法执⾏并没有实现连接,需要在listen()⽅法中调 34. 34. //⽤channel.finishConnect();才能完成连接

35. 35. channel.connect(new InetSocketAddress(ip,port));

36. 36. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 37. 37. channel.register(selector, SelectionKey.OP_CONNECT);

38. 38. } 39. 39. 40. 40. /**

41. 41. * 采⽤轮询的⽅式监听selector上是否有需要处理的事件,如果有,则进⾏处理 42. 42. * @throws IOException 43. 43. */

44. 44. @SuppressWarnings(\"unchecked\") 45. 45. public void listen() throws IOException { 46. 46. // 轮询访问selector 47. 47. while (true) {

48. 48. selector.select();

49. 49. // 获得selector中选中的项的迭代器

50. 50. Iterator ite = this.selector.selectedKeys().iterator(); 51. 51. while (ite.hasNext()) {

52. 52. SelectionKey key = (SelectionKey) ite.next(); 53. 53. // 删除已选的key,以防重复处理 54. 54. ite.remove(); 55. 55. // 连接事件发⽣

56. 56. if (key.isConnectable()) {

57. 57. SocketChannel channel = (SocketChannel) key 58. 58. .channel();

59. 59. // 如果正在连接,则完成连接

60. 60. if(channel.isConnectionPending()){ 61. 61. channel.finishConnect(); 62. 62. 63. 63. }

64. 64. // 设置成⾮阻塞

65. 65. channel.configureBlocking(false); 66. 66.

67. 67. //在这⾥可以给服务端发送信息哦

68. 68. channel.write(ByteBuffer.wrap(new String(\"向服务端发送了⼀条信息\").getBytes()));

69. 69. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 70. 70. channel.register(this.selector, SelectionKey.OP_READ); 71. 71.

72. 72. // 获得了可读的事件

73. 73. } else if (key.isReadable()) { 74. 74. read(key); 75. 75. } 76. 76.

77. 77. } 78. 78.

79. 79. } 80. 80. } 81. 81. /**

82. 82. * 处理读取服务端发来的信息 的事件 83. 83. * @param key

84. 84. * @throws IOException 85. 85. */

86. 86. public void read(SelectionKey key) throws IOException{ 87. 87. //和服务端的read⽅法⼀样 88. 88. } 89. 89. 90. 90. 91. 91. /**

92. 92. * 启动客户端测试

93. 93. * @throws IOException 94. 94. */

95. 95. public static void main(String[] args) throws IOException { 96. 96. NIOClient client = new NIOClient(); 97. 97. client.initClient(\"localhost\98. 98. client.listen(); 99. 99. } 100. 100. 101. 101. } 102.

http://blog.csdn.net/jiyiqinlovexx/article/details/42619097

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- nryq.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务