首先抛出问题:

程序1---错误版本

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6.  
  7. public class HttpSimpleServer
  8. {
  9. public void startServer() throws IOException
  10. {
  11. ServerSocket ss = new ServerSocket(10021);
  12.  
  13. Socket so = ss.accept();
  14.  
  15. InputStream in = so.getInputStream();
  16.  
  17. PrintWriter pw = new PrintWriter(so.getOutputStream(),true);
  18.  
  19. byte[] bytes = new byte[1024];
  20.  
  21. int num = 0;
  22.  
  23. while((num = in.read(bytes))!=-1)
  24. {
  25. String str = new String(bytes,0,num);
  26. if(str.trim().length() <= 0)
  27. {
  28. break;
  29. }
  30. System.out.print(str);
  31. }
  32. pw.println("<font color='red' size='7'> 今天天气真好</font>");
  33. so.close();
  34. ss.close();
  35. }
  36.  
  37. public static void main(String[] args) throws IOException
  38. {
  39. new HttpSimpleServer().startServer();
  40. }
  41. }

 

上面的代码是一个基于Java的简单的HTTP服务器,输入访问地址:http://localhost:10021/ 进行访问

服务端输出:

GET /favicon.ico HTTP/1.1
Host: localhost:10021
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

而浏览器一直卡着,看不到输出的信息。

当浏览器访问连接:http://localhost:10021/的时候,服务端将一直阻塞,浏览器收不到响应。
出现无响应的原因在于:
浏览器和你的http服务器建立连接后,先发送请求头信息,然后并不会断开连接,所以read方法就会一直阻塞,等待服务器关闭连接(只有关闭后才会返回-1)。我么知道TCP断开连接使用的是四次分手原则,之所以使用四次分手原则,是因为TCP是个全双工的管道,每一端既可以发送也可以接受,当一端调用socket.close()之后,会发送fin关闭连接的报文(表明自己不再发送数据了,但是可以接收数据),对方收到之后,发送ACK报文,确认自己已经收到所有信息,这样一端关闭了发送,一端关闭了接收,当被动关闭方发送完毕之后,发送fin给主动关闭方,从而全部关闭.

于是Http的TCP连接的主动关闭方一般都是服务端,是在服务端的Response通过TCP发送出去之后再调用socket.close()方法的。read方法会阻塞(流没结束并且没有断开信号),这是主要原因。

  1. public abstract int read()
  2. throws IOException
从输入流中读取数据的下一个字节。返回 0255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

子类必须提供此方法的一个实现。

返回:
下一个数据字节;如果到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误。

其实浏览器此时正在等待你的响应,所以我们需要自己界定请求头的范围。请求头的结束标志是两个连续的换行(这个换行是有标准规定的,必须为\r\n而不是只使用\n),即\r\n\r\n。于是我们在收到这个字符串后就可以不再读取数据,而开始写入数据了。

其他就是该服务程序是一次性的,访问之后就不能再访问了,起码应该写成可以多次访问,进一步可以修改成可以同时多次访问,即多线程访问的。

还有问题就是返回信息没有HTTP响应头部,可能会出现乱码或者浏览器无法识别等问题

程序2--修正版本1:

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. import java.util.Scanner;
  7.  
  8. /**
  9. * @author 作者 E-mail:
  10. * @version 创建时间:2015-8-27 下午09:02:16 类说明
  11. */
  12. public class HttpSimpleServer2
  13. {
  14. public void startServer() throws IOException
  15. {
  16. // 建立ServerSocket
  17. ServerSocket serso = new ServerSocket(10021);
  18. // 获取客户端对象
  19. Socket so = serso.accept();
  20. // 获取相关流对象
  21. InputStream in = so.getInputStream();
  22. PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
  23. Scanner sc = new Scanner(in);
  24. sc.useDelimiter("\r\n\r\n");
  25. if (sc.hasNext())
  26. {
  27. String header = sc.next();
  28. System.out.println(header);
  29. }
  30. // HHTP响应头部信息
  31. pw.print("HTTP/1.0 200 OK\r\n");
  32. pw.print("Content-type:text/html; charset=utf-8\r\n");
  33. pw.print("\r\n");
  34. // HTTP响应内容
  35. pw.println("<font color='red' size='7'>good</font>");
  36. sc.close();
  37. so.close();
  38. serso.close();
  39. }
  40.  
  41. public static void main(String[] args) throws IOException
  42. {
  43. new HttpSimpleServer2().startServer();
  44. }
  45. }

服务端输出:

GET / HTTP/1.1
Host: localhost:10021
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

缺点:①不能多线程访问 ②只能访问一次,程序就运行结束了

程序3---修正版本2:

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. import java.util.Scanner;
  7. import java.util.concurrent.TimeUnit;
  8.  
  9. /**
  10. * 现在这个请求仍然是阻塞的,单线程的,同一时刻只能有一个程序进行访问
  11. * @author 作者 E-mail:
  12. * @version 创建时间:2015-8-27 下午09:08:26 类说明
  13. */
  14. public class HttpSimpleServer3
  15. {
  16.  
  17. public void startServer() throws IOException, InterruptedException
  18. {
  19. // 建立ServerSocket 这里默认的backlog 是50 可以有50个请求在排队等待
  20. ServerSocket serso = new ServerSocket(10021);
  21. while (true)
  22. {
  23. // 获取客户端对象
  24. Socket so = serso.accept();
  25. // 获取相关流对象
  26. InputStream in = so.getInputStream();
  27. PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
  28. Scanner sc = new Scanner(in);
  29. sc.useDelimiter("\r\n\r\n");
  30. if (sc.hasNext())
  31. {
  32. String header = sc.next();
  33. System.out.println(header);
  34. }
  35. // HHTP响应头部信息
  36. pw.print("HTTP/1.0 200 OK\r\n");
  37. pw.print("Content-type:text/html; charset=utf-8\r\n");
  38. pw.print("\r\n");
  39. // HTTP响应内容
  40. pw.println("<font color='red' size='7'>good</font>");
  41. pw.flush();
  42. Thread.sleep(100000); //单线程程序在多个同时访问的时候就会受限
  43. sc.close();
  44. //在sc关闭之前是写不出去的,因为没有flush
  45.  
  46. so.close();
  47. }
  48. }
  49.  
  50. public static void main(String[] args) throws IOException, InterruptedException
  51. {
  52. new HttpSimpleServer3().startServer();
  53. }
  54. }

使用while循环持续监听client连接

缺点:仍旧不能多线程访问

程序4:----修正版本3

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. import java.util.Scanner;
  7.  
  8. /**
  9. * @author 作者 E-mail:
  10. * @version 创建时间:2015-8-27 下午09:16:50 类说明
  11. */
  12.  
  13. class Runner implements Runnable
  14. {
  15. private final Socket socket;
  16.  
  17. public Runner(Socket socket)
  18. {
  19. this.socket = socket;
  20. }
  21.  
  22. @Override
  23. public void run()
  24. {
  25. // 获取相关流对象
  26.  
  27. try
  28. {
  29. InputStream in = socket.getInputStream();
  30. PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
  31. Scanner sc = new Scanner(in);
  32. sc.useDelimiter("\r\n\r\n");
  33. if (sc.hasNext())
  34. {
  35. String header = sc.next();
  36. System.out.println(header);
  37. }
  38. // HTTP响应头部信息
  39. pw.print("HTTP/1.0 200 OK\r\n");
  40. pw.print("Content-type:text/html; charset=utf-8\r\n");
  41. pw.print("\r\n");
  42. // HTTP响应内容
  43. pw.println("<font color='red' size='7'>good</font>");
  44. pw.flush();
  45. try
  46. {
  47. Thread.sleep(100000);
  48. }
  49. catch(InterruptedException e)
  50. {
  51. e.printStackTrace();
  52. }
  53. sc.close(); //服务器端的关闭请求。
  54. socket.close();
  55. }
  56. catch(IOException e1)
  57. {
  58. // TODO Auto-generated catch block
  59. e1.printStackTrace();
  60. }
  61.  
  62. }
  63.  
  64. }
  65. //实现了多线程的访问,但是效率明显有点低哈哈哈哈
  66. public class HttpSimpleServer4
  67. {
  68. public void startServer() throws IOException
  69. {
  70. // 建立ServerSocket 这里默认的backlog 是50 可以有50个请求在排队等待
  71. ServerSocket serso = new ServerSocket(10021);
  72. while (true)
  73. {
  74. // 获取客户端对象
  75. Socket so = serso.accept();
  76. Runnable runner = new Runner(so);
  77. Thread thread = new Thread(runner);
  78. // run就相当于在本线程当中调用,start才是启动的新线程
  79. thread.start();
  80. }
  81. }
  82.  
  83. public static void main(String[] args) throws IOException,
  84. InterruptedException
  85. {
  86. new HttpSimpleServer4().startServer();
  87. }
  88. }

实现了多线程访问,

缺点:是阻塞式的,每个访问都要启用一个线程,没有数据输入线程就会阻塞,线程重复创建和销毁

程序5---修正版本4

草稿

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  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. import java.util.Scanner;
  12. import java.util.Set;
  13.  
  14. /**
  15. * @author 作者 E-mail:
  16. * @version 创建时间:2015-8-27 下午09:37:25 类说明
  17. */
  18. interface Handler
  19. {
  20. void doHandle(SelectionKey key);
  21. }
  22.  
  23. class AcceptHandler implements Handler
  24. {
  25.  
  26. @Override
  27. public void doHandle(SelectionKey key)
  28. {
  29. ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
  30. SocketChannel socketChannel = null;
  31. // 在非阻塞模式下,serverSocketChannel.accept()有可能返回null
  32. // 判断socketChannel是否为null,可以使程序更加健壮,避免NullPointException
  33. try
  34. {
  35. socketChannel = serverSocketChannel.accept();
  36. socketChannel.configureBlocking(false);
  37. if (socketChannel == null)
  38. return;
  39. System.out.println("接收到客户链接,来自:" + socketChannel.socket().getInetAddress() + ":" + socketChannel.socket().getPort());
  40. RequestHandler requestHandler = new RequestHandler(socketChannel);
  41. socketChannel.register(key.selector(), SelectionKey.OP_READ, requestHandler);
  42. }
  43. catch(IOException ex)
  44. {
  45. ex.printStackTrace();
  46.  
  47. }
  48.  
  49. }
  50.  
  51. }
  52.  
  53. class RequestHandler implements Handler
  54. {
  55. private SocketChannel socketChannel = null;
  56.  
  57. public RequestHandler(SocketChannel socketChannel)
  58. {
  59. this.socketChannel = socketChannel;
  60. }
  61.  
  62. @Override
  63. public void doHandle(SelectionKey key)
  64. {
  65. try
  66. {
  67. ByteBuffer buffer = ByteBuffer.allocate(1024);
  68. socketChannel.read(buffer);
  69. if(buffer.position()!=0)
  70. {
  71. System.out.println(new String(buffer.array()));
  72. }
  73. else
  74. {
  75. // 这里可能出现问题
  76. System.out.println(buffer.toString());
  77. }
  78. buffer.flip();
  79. // System.out.println(buffer)
  80. // buffer.wrap(array)
  81. // // HTTP响应头部信息
  82. // S
  83. // pw.print("HTTP/1.0 200 OK\r\n");
  84. // pw.print("Content-type:text/html; charset=utf-8\r\n");
  85. // pw.print("\r\n");
  86. // // HTTP响应内容
  87. // pw.println("<font color='red' size='7'>good</font>");
  88. // pw.flush();
  89. }
  90. catch(IOException ex)
  91. {
  92. ex.printStackTrace();
  93. }
  94.  
  95. }
  96. }
  97.  
  98. public class HttpSimpleServer5
  99. {
  100. private Selector selector = null;
  101.  
  102. private ServerSocketChannel serverSocketChannel = null;
  103.  
  104. private int port = 10021;
  105.  
  106. public HttpSimpleServer5() throws IOException
  107. {
  108. // 创建一个Selector对象
  109. selector = Selector.open();
  110. // 创建一个ServerSocketChannel对象
  111. serverSocketChannel = ServerSocketChannel.open();
  112. // 使ServerSocketChannel工作处于非阻塞模式
  113. serverSocketChannel.configureBlocking(false);
  114. // 使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时
  115. // 可以顺利的绑定到相同的端口
  116. serverSocketChannel.socket().setReuseAddress(true);
  117. // 把服务器进程与一个本地端口绑定
  118. serverSocketChannel.socket().bind(new InetSocketAddress(10021));
  119. }
  120.  
  121. void startServer() throws IOException
  122. {
  123. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, new AcceptHandler());
  124. while (selector.select() > 0)
  125. {
  126. // 获得Selector的selector-keys集合
  127. Set<SelectionKey> readKeys = selector.selectedKeys();
  128. Iterator<SelectionKey> it = readKeys.iterator();
  129. while (it.hasNext())
  130. {
  131. SelectionKey key = it.next();
  132. it.remove();
  133.  
  134. // 由Handler处理连接就绪事件
  135. final Handler handler = (Handler) key.attachment();
  136. handler.doHandle(key);
  137.  
  138. }
  139. }
  140. }
  141.  
  142. public static void main(String[] args) throws IOException
  143. {
  144. new HttpSimpleServer5().startServer();
  145. }
  146. }

  

基于Java的Http服务器几种模式演进的更多相关文章

  1. CentOS 6 搭建SVN支持httpd和svnserve独立服务器两种模式 以及邮件配置

    Linux下SVN服务器同时支持Apache的http和svnserve独立服务器两种模式且使用相同的访问权限账号 服务器操作系统:CentOS 6.x 1.在服务器上安装配置SVN服务: 2.配置S ...

  2. ftp服务器三种模式

    一.匿名开放模式(最不安全) 1.[root@localhost ~]# vim  /etc/vsftpd/vsftpd.conf  (主配置) anonymous_enable=YES    //允 ...

  3. 基于Java的二叉树的三种遍历方式的递归与非递归实现

    二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...

  4. 第三节:Windows平台部署Asp.Net Core应用(基于IIS和Windows服务两种模式)

    一. 简介 二. 文件系统发布至IIS 三. Web部署发布至IIS 四. FTP发布至IIS 五. Windows服务的形式发布 ! 作       者 : Yaopengfei(姚鹏飞) 博客地址 ...

  5. Java实现http服务器(一)

    基于Java实现Http服务器有多种多样的方法 一种轻量级的方式是使用JDK内置的com.sun.net.httpserver包下和sun.net.httpserver包下类提供的方法构建,该方法轻便 ...

  6. 基于JAVA语言的多线程技术

    1.简介 多线程技术属于操作系统范围内的知识: 进程与线程 可以这么理解,一个应用程序就是一个进程,在一个进程中包含至少一个线程:进程就是线程的容器,真正工作.处理任务的是线程. 进程是操作系统分配资 ...

  7. Spring核心技术(十二)——基于Java的容器配置(二)

    使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bea ...

  8. 基于Java Mina框架的部标808服务器设计和开发

    在开发部标GPS平台中,部标808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言. 我们为客户开发的部标服务器基于Min ...

  9. 基于Java Mina框架的部标jt808服务器设计和开发

    在开发部标GPS平台中,部标jt808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言.需要购买jt808GPS服务器源码 ...

随机推荐

  1. jQuery 简单归纳总结

    jQuery语法是为HTML元素的选取编制的,能够对元素运行某些操作. 基础语法是:$(selector).action() +美元符号定义 jQuery +选择符(selector)"查询 ...

  2. boost库在工作(37)网络UDP服务端之七

    前面介绍的都是网络TCP的服务器和客户端,其实还有UDP的服务器和客户端,同时也有同步和异步之分.UDP与TCP最大的区别,就是TCP是基于连接的,而UDP是无连接的.这里所谓的连接是指对方中断服务时 ...

  3. 如何获取Android系统中申请对象的信息

    最近一直在做有关内存方面的优化工作,在做优化的过程,除了关注内存的申请量以及GC的情况之外,我们经常需要想方法找出是那些对象占用了大量内存,以及他们是如何导致GC的,这意味着我们需要获取对象申请的信息 ...

  4. iOS常见的几种延时执行的方法

    1.performSelector [self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay ...

  5. 转--DataTable 修改列名 删除列 调整列顺序

    DataTable myDt =dt; //删除列 myDt.Columns.Remove("minArea"); myDt.Columns.Remove("maxAre ...

  6. PHP 给前面或者后面添加0补位

    相信大家一定遇到这样的问题,因为PHP是弱类型的,所以进行排序的时候,有时候很胃疼 所以这里就需要将位数进行统一后进行处理 一般都是将末尾添加0进行补位 方法1 :  str_pad — 使用另一个字 ...

  7. python基础知识九

    sys模块 sys模块包含系统对应的功能.我们已经学习了sys.argv列表,它包含命令行参数. 命令行参数 例14.1 使用sys.argv #!/usr/bin/python # Filename ...

  8. 对于百川SDK签名验证的问题

    SDK是要在wantu.taobao.com生成的.而生成这个SDK其实是要上传一个apk,而这个上传其实就是取他的签名而已.验证就是那张yw222那张图片.重点是你上传的apk的签名是不是跟你的生成 ...

  9. UITabBar-UITabBarItem图片的背景颜色属性和文字的颜色大小设置

    UITabBarItem设置的图片选中状态下默认的是蓝色,如何改变它的颜色为图片自带的颜色呢? typedef NS_ENUM(NSInteger, UIImageRenderingMode) { / ...

  10. C++文件操作详解(ifstream、ofstream、fstream)

    C++ 通过以下几个类支持文件的输入输出: ofstream: 写操作(输出)的文件类 (由ostream引申而来) ifstream: 读操作(输入)的文件类(由istream引申而来) fstre ...