《Netty权威指南》第2章NIO入门,本章中,我们会分别对JDK的BIO、NIO和JDK1.7最新提供的NIO2.0的使用进行详细说明,通过流程图和代码讲解,让大家体会到随着Java I/O类库的不断发展和改进,基于Java的网络编程会变得越来越简单,随着异步I/O功能的增强,基于Java NIO开发的网络服务器甚至不逊色于采用C++开发的网络程序。本节为大家介绍AIO创建的TimeServer源码分析。
作者:李林锋来源:电子工业出版社|2014-07-09 11:57
收藏
分享
9月15日技术沙龙 | 如何将智能化和运维工作相结合,实现智能运维!
2.4.1 AIO创建的TimeServer源码分析(1)
首先看下时间服务器的主函数。
代码清单2-11 AIO时间服务器服务端 TimeClientHandle
- 10. public class TimeServer {
- 11.
- 12. /**
- 13. * @param args
- 14. * @throws IOException
- 15. */
- 16. public static void main(String[] args) throws IOException {
- 17. int port = 8080;
- 18. if (args != null && args.length > 0) {
- 19. try {
- 20. port = Integer.valueOf(args[0]);
- 21. } catch (NumberFormatException e) {
- 22. // 采用默认值
- 23. }
- 24. }
- 25. AsyncTimeServerHandler timeServer=new AsyncTimeServerHandler(port);
- 26. new Thread(timeServer, "AIO-AsyncTimeServerHandler-001").start();
- 27. }
- 28. }
我们直接从第25行开始看,首先创建异步的时间服务器处理类,然后启动线程将AsyncTimeServerHandler拉起,代码如下。
代码清单2-12 AIO时间服务器服务端
- 13. public class AsyncTimeServerHandler implements Runnable {
- 14.
- 15. private int port;
- 16.
- 17. CountDownLatch latch;
- 18. AsynchronousServerSocketChannel asynchronousServerSocketChannel;
- 19.
- 20. public AsyncTimeServerHandler(int port) {
- 21. this.port = port;
- 22. try {
- 23. asynchronousServerSocketChannel = AsynchronousServerSocketChannel
- 24. .open();
- 25. asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
- 26. System.out.println("The time server is start in port : " + port);
- 27. } catch (IOException e) {
- 28. e.printStackTrace();
- 29. }
- 30. }
- 31.
- 32. /*
- 33. * (non-Javadoc)
- 34. *
- 35. * @see java.lang.Runnable#run()
- 36. */
- 37. @Override
- 38. public void run() {
- 39.
- 40. latch = new CountDownLatch(1);
- 41. doAccept();
- 42. try {
- 43. latch.await();
- 44. } catch (InterruptedException e) {
- 45. e.printStackTrace();
- 46. }
- 47. }
- 48.
- 49. public void doAccept() {
- 50. asynchronousServerSocketChannel.accept(this,
- 51. new AcceptCompletionHandler());
- 52. }
我们重点对AsyncTimeServerHandler进行分析。首先看20~27行,在构造方法中,我们首先创建一个异步的服务端通道AsynchronousServerSocketChannel,然后调用它的bind方法绑定监听端口,如果端口合法且没被占用,绑定成功,打印启动成功提示到控制台。
在线程的run方法中,第40行我们初始化CountDownLatch对象,它的作用是在完成一组正在执行的操作之前,允许当前的线程一直阻塞。在本例程中,我们让线程在此阻塞,防止服务端执行完成退出。在实际项目应用中,不需要启动独立的线程来处理AsynchronousServerSocketChannel,这里仅仅是个demo演示。
第41行用于接收客户端的连接,由于是异步操作,我们可以传递一个CompletionHandler <AsynchronousSocketChannel,? super A>类型的handler实例接收accept操作成功的通知消息,在本例程中我们通过AcceptCompletionHandler实例作为handler来接收通知消息,下面继续对AcceptCompletionHandler进行分析。
代码清单2-13 AIO时间服务器服务端 AcceptCompletionHandler
- 14.
- 15. @Override
- 16. public void completed(AsynchronousSocketChannel result,
- 17. AsyncTimeServerHandler attachment) {
- 18. attachment.asynchronousServerSocketChannel.accept(attachment, this);
- 19. ByteBuffer buffer = ByteBuffer.allocate(1024);
- 20. result.read(buffer, buffer, new ReadCompletionHandler(result));
- 21. }
- 22.
- 23. @Override
- 24. public void failed(Throwable exc,AsyncTimeServerHandler attachment) {
- 25. exc.printStackTrace();
- 26. attachment.latch.countDown();
- 27. }
- 28. }
CompletionHandler有两个方法,分别如下。
public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment);
public void failed(Throwable exc, AsyncTimeServerHandler attachment)。
下面我们分别对这两个接口的实现进行分析。首先看completed接口的实现,代码18~20行,我们从attachment获取成员变量AsynchronousServerSocketChannel,然后继续调用它的accept方法。有的读者在此可能会心存疑惑:既然已经接收客户端成功了,为什么还要再次调用accept方法呢?原因是这样的:当我们调用AsynchronousServerSocketChannel的accept方法后,如果有新的客户端连接接入,系统将回调我们传入的CompletionHandler实例的completed方法,表示新的客户端已经接入成功,因为一个AsynchronousServerSocket Channel可以接收成千上万个客户端,所以我们需要继续调用它的accept方法,接收其他的客户端连接,最终形成一个循环。每当接收一个客户读连接成功之后,再异步接收新的客户端连接