2.4.1 AIO创建的TimeServer源码分析(2)
链路建立成功之后,服务端需要接收客户端的请求消息,在代码第19行我们创建新的ByteBuffer,预分配1M的缓冲区。第20行我们通过调用AsynchronousSocketChannel的read方法进行异步读操作。下面我们看看异步read方法的参数。
ByteBuffer dst:接收缓冲区,用于从异步Channel中读取数据包;
A attachment:异步Channel携带的附件,通知回调的时候作为入参使用;
CompletionHandler<Integer, super A>:接收通知回调的业务handler,本例程中为ReadCompletionHandler。
下面我们继续对ReadCompletionHandler进行分析。
代码清单2-14 AIO时间服务器服务端 ReadCompletionHandler
- 8.
- 9. /**
- 10. * @author lilinfeng
- 11. * @date 2014年2月16日
- 12. * @version 1.0
- 13. */
- 14. public class ReadCompletionHandler implements
- 15. CompletionHandler<Integer, ByteBuffer> {
- 16.
- 17. private AsynchronousSocketChannel channel;
- 18.
- 19. public ReadCompletionHandler(AsynchronousSocketChannel channel) {
- 20. if (this.channel == null)
- 21. this.channel = channel;
- 22. }
- 23.
- 24. @Override
- 25. public void completed(Integer result, ByteBuffer attachment) {
- 26. attachment.flip();
- 27. byte[] body = new byte[attachment.remaining()];
- 28. attachment.get(body);
- 29. try {
- 30. String req = new String(body, "UTF-8");
- 31. System.out.println("The time server receive order : " + req);
- 32. String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(req) new java.util.Date(
- 33. System.currentTimeMillis()).toString() : "BAD ORDER";
- 34. doWrite(currentTime);
- 35. } catch (UnsupportedEncodingException e) {
- 36. e.printStackTrace();
- 37. }
- 38. }
- 39.
- 40. private void doWrite(String currentTime) {
- 41. if (currentTime != null && currentTime.trim().length() > 0) {
- 42. byte[] bytes = (currentTime).getBytes();
- 43. ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
- 44. writeBuffer.put(bytes);
- 45. writeBuffer.flip();
- 46. channel.write(writeBuffer, writeBuffer,
- 47. new CompletionHandler<Integer, ByteBuffer>() {
- 48. @Override
- 49. public void completed(Integer result, ByteBuffer buffer) {
- 50. // 如果没有发送完成,继续发送
- 51. if (buffer.hasRemaining())
- 52. channel.write(buffer, buffer, this);
- 53. }
- 54.
- 55. @Override
- 56. public void failed(Throwable exc, ByteBuffer attachment) {
- 57. try {
- 58. channel.close();
- 59. } catch (IOException e) {
- 60. // ingnore on close
- 61. }
- 62. }
- 63. });
- 64. }
- 65. }
- 66.
- 67. @Override
- 68. public void failed(Throwable exc, ByteBuffer attachment) {
- 69. try {
- 70. this.channel.close();
- 71. } catch (IOException e) {
- 72. e.printStackTrace();
- 73. }
- 74. }
- 75. }
首先看构造方法,我们将AsynchronousSocketChannel通过参数传递到ReadCompletion Handler中当作成员变量来使用,主要用于读取半包消息和发送应答。本例程不对半包读写进行具体说明,对此感兴趣的读者可以关注后续章节对Netty半包处理的专题介绍。我们继续看代码,第25~38行是读取到消息后的处理,首先对attachment进行flip操作,为后续从缓冲区读取数据做准备。根据缓冲区的可读字节数创建byte数组,然后通过new String方法创建请求消息,对请求消息进行判断,如果是"QUERY TIME ORDER"则获取当前系统服务器的时间,调用doWrite方法发送给客户端。下面我们对doWrite方法进行详细分析。
跳到代码第41行,首先对当前时间进行合法性校验,如果合法,调用字符串的解码方法将应答消息编码成字节数组,然后将它复制到发送缓冲区writeBuffer中,最后调用AsynchronousSocketChannel的异步write方法。正如前面介绍的异步read方法一样,它也有三个与read方法相同的参数,在本例程中我们直接实现write方法的异步回调接口CompletionHandler。代码跳到第51行,对发送的writeBuffer进行判断,如果还有剩余的字节可写,说明没有发送完成,需要继续发送,直到发送成功。
最后,我们关注下failed方法,它的实现很简单,就是当发生异常的时候,对异常Throwable进行判断,如果是I/O异常,就关闭链路,释放资源,如果是其他异常,按照业务自己的逻辑进行处理。本例程作为简单demo,没有对异常进行分类判断,只要发生了读写异常,就关闭链路,释放资源。
异步非阻塞I/O版本的时间服务器服务端已经介绍完毕,下面我们继续看客户端的实现。
喜欢的朋友可以添加我们的微信账号:
51CTO读书频道二维码

51CTO读书频道活动讨论群:342347198