2.1.2 同步阻塞式I/O创建的TimeServer源码分析
代码清单2-1 同步阻塞I/O的TimeServer
(备注:以下代码行号均对应源代码中实际行号。)
- 1. package com.phei.netty.bio;
- 2. import java.io.IOException;
- 3. import java.net.ServerSocket;
- 4. import java.net.Socket;
- 5. /**
- 6. * @author lilinfeng
- 7. * @date 2014年2月14日
- 8. * @version 1.0
- 9. */
- 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.
- 20. try {
- 21. port = Integer.valueOf(args[0]);
- 22. } catch (NumberFormatException e) {
- 23. // 采用默认值
- 24. }
- 25.
- 26. }
- 27. ServerSocket server = null;
- 28. try {
- 29. server = new ServerSocket(port);
- 30. System.out.println("The time server is start in port : " + port);
- 31. Socket socket = null;
- 32. while (true) {
- 33. socket = server.accept();
- 34. new Thread(new TimeServerHandler(socket)).start();
- 35. }
- 36. } finally {
- 37. if (server != null) {
- 38. System.out.println("The time server close");
- 39. server.close();
- 40. server = null;
- 41. }
- 42. }
- 43. }
- 44. }
TimeServer根据传入的参数设置监听端口,如果没有入参,使用默认值8080,29行通过构造函数创建ServerSocket,如果端口合法且没有被占用,服务端监听成功。32~35行通过一个无限循环来监听客户端的连接,如果没有客户端接入,则主线程阻塞在ServerSocket的accept操作上。启动TimeServer,通过JvisualVM打印线程堆栈,我们可以发现主程序确实阻塞在accept操作上,如图2-2所示。

当有新的客户端接入的时候,执行代码34行,以Socket为参数构造TimeServerHandler对象,TimeServerHandler是一个Runnable,使用它为构造函数的参数创建一个新的客户端线程处理这条Socket链路。下面我们继续分析TimeServerHandler的代码。
代码清单2-2 同步阻塞I/O的TimeServerHandler
- 13. public class TimeServerHandler implements Runnable {
- 14.
- 15. private Socket socket;
- 16.
- 17. public TimeServerHandler(Socket socket) {
- 18. this.socket = socket;
- 19. }
- 20.
- 21. /*
- 22. * (non-Javadoc)
- 23. *
- 24. * @see java.lang.Runnable#run()
- 25. */
- 26. @Override
- 27. public void run() {
- 28. BufferedReader in = null;
- 29. PrintWriter out = null;
- 30. try {
- 31. in = new BufferedReader(new InputStreamReader(
- 32. this.socket.getInputStream()));
- 33. out = new PrintWriter(this.socket.getOutputStream(), true);
- 34. String currentTime = null;
- 35. String body = null;
- 36. while (true) {
- 37. body = in.readLine();
- 38. if (body == null)
- 39. break;
- 40. System.out.println("The time server receive order : " + body);
- 41. currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) new java.util.Date(
- 42. System.currentTimeMillis()).toString() : "BAD ORDER";
- 43. out.println(currentTime);
- 44. }
- 45.
- 46. } catch (Exception e) {
- 47. if (in != null) {
- 48. try {
- 49. in.close();
- 50. } catch (IOException e1) {
- 51. e1.printStackTrace();
- 52. }
- 53. }
- 54. if (out != null) {
- 55. out.close();
- 56. out = null;
- 57. }
- 58. if (this.socket != null) {
- 59. try {
- 60. this.socket.close();
- 61. } catch (IOException e1) {
- 62. e1.printStackTrace();
- 63. }
- 64. this.socket = null;
- 65. }
- 66. }
- 67. }
- 68. }
37行通过BufferedReader读取一行,如果已经读到了输入流的尾部,则返回值为null,退出循环。如果读到了非空值,则对内容进行判断,如果请求消息为查询时间的指令"QUERY TIME ORDER"则获取当前最新的系统时间,通过PrintWriter的println函数发送给客户端,最后退出循环。代码47~64行释放输入流、输出流、和Socket套接字句柄资源,最后线程自动销毁并被虚拟机回收。
在下一个小结,我们将介绍同步阻塞I/O的客户端代码,然后分别运行服务端和客户端,查看下程序的运行结果。
喜欢的朋友可以添加我们的微信账号:
51CTO读书频道二维码

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