用Java编写你自己的简单HTTP服务器
2014-12-04 16:31
239 查看
来源:http://blog.csdn.net/yanghua_kobe/article/details/7296156
HTTP是个大协议,完整功能的HTTP服务器必须响应资源请求,将URL转换为本地系统的资源名。响应各种形式的HTTP请求(GET、POST等)。处理不存在的文件请求,返回各种形式的状态码,解析MIME类型等。但许多特定功能的HTTP服务器并不需要所有这些功能。例如,很多网站只是想显示“建设中“的消息。很显然,Apache对于这样的网站是大材小用了。这样的网站完全可以使用只做一件事情的定制服务器。Java网络类库使得编写这样的单任务服务器轻而易举。 定制服务器不只是用于小网站。大流量的网站如Yahoo,也使用定制服务器,因为与一般用途的服务器相比,只做一件事情的服务器通常要快得多。针对某项任务来优化特殊用途的服务器很容易;其结果往往比需要响应很多种请求的一般用途服务器高效得多。例如,对于重复用于多页面或大流量页面中的图标和图片,用一个单独的服务器处理会更好(并且还可以避免在请求时携带不必要的Cookie,因而可以减少请求/响应数据,从而减少下载带宽,提升速度);这个服务器在启动时把所有图片文件读入内存,从RAM中直接提供这些文件,而不是每次请求都从磁盘上读取。此外,如果你不想在包含这些图片的页面请求之外单独记录这些图片,这个单独服务器则会避免在日志记录上浪费时间。
RequestProcessor.java
测试结果:
图1
JHTTP类的main()方法根据args[0]设置文档的根目录。端口从args[1]读取,或者使用默认的80.然后构造一个新的JHTTP线程并启动。此JHTTP线程生成50个RequestProcessor线程处理请求,每个线程在可用时从RequestProcessor池获取入站连接请求。JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中。每个连接由下例所示的RequestProcessor类的run()方法处理。此方法将一直等待,直到从池中得到一个Socket。一旦得到Socket,就获取输入和输出流,并链接到阅读器和书写器。接着的处理,除了多出文档目录、路径的处理,其他的同单文件服务器。
最后,花点时间考虑一下可以采用什么方法来优化此服务器。如果真的希望使用JHTTP运行高流量的网站,还可以做一些事情来加速此服务器。第一点也是最重要的一点就是使用即时编译器(JIT),如HotSpot。JIT可以将程序的性能提升大约一个数量级。第二件事就是实现智能缓存。记住接受的请求,将最频繁的请求文件的数据存储在Hashtable中,使之保存在内存中。使用低优先级的线程更新此缓存。
HTTP是个大协议,完整功能的HTTP服务器必须响应资源请求,将URL转换为本地系统的资源名。响应各种形式的HTTP请求(GET、POST等)。处理不存在的文件请求,返回各种形式的状态码,解析MIME类型等。但许多特定功能的HTTP服务器并不需要所有这些功能。例如,很多网站只是想显示“建设中“的消息。很显然,Apache对于这样的网站是大材小用了。这样的网站完全可以使用只做一件事情的定制服务器。Java网络类库使得编写这样的单任务服务器轻而易举。 定制服务器不只是用于小网站。大流量的网站如Yahoo,也使用定制服务器,因为与一般用途的服务器相比,只做一件事情的服务器通常要快得多。针对某项任务来优化特殊用途的服务器很容易;其结果往往比需要响应很多种请求的一般用途服务器高效得多。例如,对于重复用于多页面或大流量页面中的图标和图片,用一个单独的服务器处理会更好(并且还可以避免在请求时携带不必要的Cookie,因而可以减少请求/响应数据,从而减少下载带宽,提升速度);这个服务器在启动时把所有图片文件读入内存,从RAM中直接提供这些文件,而不是每次请求都从磁盘上读取。此外,如果你不想在包含这些图片的页面请求之外单独记录这些图片,这个单独服务器则会避免在日志记录上浪费时间。
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.Socket; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; /** * @Title: RequestProcessor.java * @Package * @author 任伟 * @date 2014-12-4 下午1:42:22 * @version V1.0 */ /** * @ClassName: RequestProcessor * @Description: 请求处理程序 * @author 任伟 * @date 2014-12-4 下午1:42:22 */ public class RequestProcessor implements Runnable { private static List pool = new LinkedList(); private File documentRootDirectory; private String indexFileName = "index.html"; // 构造方法 public RequestProcessor(File documentRootDirectory, String indexFileName) { if (documentRootDirectory.isFile()) { throw new IllegalArgumentException(); } this.documentRootDirectory = documentRootDirectory; try { this.documentRootDirectory = documentRootDirectory .getCanonicalFile(); } catch (IOException e) { } if (indexFileName != null) { this.indexFileName = indexFileName; } } // 向请求池加入请求 public static void processRequest(Socket request) { synchronized (pool) { pool.add(pool.size(), request); pool.notifyAll(); } } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { // 无限循环处理 while (true) { // 先进性安全性检测,然后从连接池获取一个链接 Socket connection; synchronized (pool) { while (pool.isEmpty()) { try { pool.wait(); } catch (Exception e) { } } connection = (Socket) pool.remove(0); } // 开始处理 try { // 获得输入输出流 OutputStream raw = new BufferedOutputStream( connection.getOutputStream()); Writer out = new OutputStreamWriter(raw); Reader in = new InputStreamReader(new BufferedInputStream( connection.getInputStream())); // 拼接请求字符串 StringBuffer request = new StringBuffer(80); while (true) { int c = in.read(); if (c == '\t' || c == '\n' || c == -1) { break; } request.append((char) c); } // 记录日志 eg: // localhost:port/a/b/c/index.html // GET /a/b/c/index.html HTTP/1.1 String get = request.toString(); System.out.println(get); // 分析请求 StringTokenizer st = new StringTokenizer(get); String method = st.nextToken();// 请求的方法 GET String fileName;// 请求的文件名 String version = "";// 协议版本 String contentType;// 相应返回的内容类型 if (method.equals("GET")) {// 方法是“GET” fileName = st.nextToken(); if (fileName.endsWith("/")) { fileName += indexFileName; } contentType = guessContentTypeFromName(fileName); if (st.hasMoreTokens()) { version = st.nextToken(); } // 根据文件目录读出文件,并返回响应 File theFile = new File(documentRootDirectory, fileName.substring(1, fileName.length())); String root = documentRootDirectory.getPath(); if (theFile.canRead() && theFile.getCanonicalPath().startsWith(root)) {// 读取文件成功 DataInputStream fis = new DataInputStream( new BufferedInputStream(new FileInputStream( theFile))); byte[] theData = new byte[(int) theFile.length()]; fis.readFully(theData); fis.close(); // HTTP请求,返回响应头 if (version.startsWith("HTTP")) { out.write("HTTP/1.0 200 OK\r\n"); Date now = new Date(); out.write("Date: " + now + "\r\n"); out.write("Server: JHTTP 1.0\r\n"); out.write("Content-length: " + theData.length + "\r\n"); out.write("Content-Type: " + contentType + "\r\n\r\n"); out.flush(); } raw.write(theData); raw.flush(); } else {// 读取文件不成功 if (version.startsWith("HTTP")) { // 是HTTP请求返回 响应头 404 out.write("HTTP/1.0 404 File Not Found\r\n"); Date now = new Date(); out.write("Date: " + now + "\r\n"); out.write("Server: JHTTP 1.0\r\n"); out.write("Content-Type: text/html\r\n\r\n"); } out.write("<HTML>\r\n"); out.write("<HEAD><TITLE>File Not Found</TITLE></HRAD>\r\n"); out.write("<BODY>\r\n"); out.write("<H1>HTTP Error 404: File Not Found</H1>"); out.write("</BODY></HTML>\r\n"); out.flush(); } } else {// 方法不是“GET” if (version.startsWith("HTTP")) { out.write("HTTP/1.0 501 Not Implemented\r\n"); Date now = new Date(); out.write("Date: " + now + "\r\n"); out.write("Server: JHTTP 1.0\r\n"); out.write("Content-Type: text/html\r\n\r\n"); } out.write("<HTML>\r\n"); out.write("<HEAD><TITLE>Not Implemented</TITLE></HRAD>\r\n"); out.write("<BODY>\r\n"); out.write("<H1>HTTP Error 501: Not Implemented</H1>"); out.write("</BODY></HTML>\r\n"); out.flush(); } } catch (Exception e) { // TODO: handle exception } finally { try { connection.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 根据文件名字猜测返回的文件的内容类型 public static String guessContentTypeFromName(String name) { if (name.endsWith(".html") || name.endsWith(".htm")) { return "text/html"; } else if (name.endsWith(".txt") || name.endsWith(".java")) { return "text/plain"; } else if (name.endsWith(".class")) { return "application/octet-stream"; } else if (name.endsWith(".gif")) { return "image/gif"; } else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) { return "image/jpeg"; } else if (name.endsWith(".png")) { return "image/png"; } else { return "text/plain"; } } }
RequestProcessor.java
测试结果:
图1
JHTTP类的main()方法根据args[0]设置文档的根目录。端口从args[1]读取,或者使用默认的80.然后构造一个新的JHTTP线程并启动。此JHTTP线程生成50个RequestProcessor线程处理请求,每个线程在可用时从RequestProcessor池获取入站连接请求。JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中。每个连接由下例所示的RequestProcessor类的run()方法处理。此方法将一直等待,直到从池中得到一个Socket。一旦得到Socket,就获取输入和输出流,并链接到阅读器和书写器。接着的处理,除了多出文档目录、路径的处理,其他的同单文件服务器。
最后,花点时间考虑一下可以采用什么方法来优化此服务器。如果真的希望使用JHTTP运行高流量的网站,还可以做一些事情来加速此服务器。第一点也是最重要的一点就是使用即时编译器(JIT),如HotSpot。JIT可以将程序的性能提升大约一个数量级。第二件事就是实现智能缓存。记住接受的请求,将最频繁的请求文件的数据存储在Hashtable中,使之保存在内存中。使用低优先级的线程更新此缓存。
相关文章推荐
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- 用Java编写你自己的简单HTTP服务器
- [置顶] 用Java编写你自己的简单HTTP服务器
- JAVA编写简单的http服务器
- 一个简单的http服务器(Java)
- 利用Java编写简单的WebService实例
- 问题1:java中没有实现这种“byte a = 0xB2 --> String b = “B2””转换的简单实现需要自己实现。 答:自己编写的转换函数,思路将byte的高低4位分开,分别转换为对应的字符然后合成返回的字符串。 java 代码 1.
- 简单使用Eclipse编写第一个Java程序
- 利用Java编写简单的WebService实例
- java编写一个简单记事本
- 作业——java编写一个简单的移动方块游戏
- 编写存储过程,并且使用java代码调用存储过程的简单例子