JHTTP类的main()方法根据args[0]设置文档的根目录。端口从args[1]读取,或者使用默认的80.然后构造一个新的JHTTP线程并启动。此JHTTP线程生成50个RequestProcessor线程处理请求,每个线程在可用时从RequestProcessor池获取入站连接请求。JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中。每个连接由下例所示的RequestProcessor类的run()方法处理。此方法将一直等待,直到从池中得到一个Socket。一旦得到Socket,就获取输入和输出流,并链接到阅读器和书写器。接着的处理,除了多出文档目录、路径的处理,其他的同单文件服务器。
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.List; import java.util.LinkedList; import java.util.StringTokenizer; 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(); } } @Override public void run() { //安全性检测 String root=documentRootDirectory.getPath(); while (true) { Socket connection; synchronized (pool) { while (pool.isEmpty()) { try { pool.wait(); } catch (InterruptedException e) { } } connection=(Socket)pool.remove(0); } try { String fileName; String contentType; OutputStream raw=new BufferedOutputStream(connection.getOutputStream()); Writer out=new OutputStreamWriter(raw); Reader in=new InputStreamReader(new BufferedInputStream(connection.getInputStream()), "ASCII"); StringBuffer request=new StringBuffer(80); while (true) { int c=in.read(); if (c=='\t'||c=='\n'||c==-1) { break; } request.append((char)c); } String get=request.toString(); //记录日志 System.out.println(get); StringTokenizer st=new StringTokenizer(get); String method=st.nextToken(); String version=""; if (method=="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())); 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(); 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 ")) { 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.flush(); } 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"); } }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.flush(); } 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"); } } catch (IOException e) { }finally{ try { connection.close(); } catch (IOException e2) { } } } } 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(".gif")) { return "image/gif"; }else if (name.endsWith(".class")) { return "application/octet-stream"; }else if (name.endsWith(".jpg")||name.endsWith(".jpeg")) { return "image/jpeg"; }else { return "text/plain"; } } }
不足与改善:这个服务器可以提供一定的功能,但仍然十分简单,还可以添加以下的一些特性:
(1) 服务器管理界面
(2) 支持CGI程序和Java Servlet API
(3) 支持其他请求方法
(4) 常见Web日志文件格式的日志文件
(5) 支持多文档根目录,这样各用户可以有自己的网站
最后,花点时间考虑一下可以采用什么方法来优化此服务器。如果真的希望使用JHTTP运行高流量的网站,还可以做一些事情来加速此服务器。第一点也是最重要的一点就是使用即时编译器(JIT),如HotSpot。JIT可以将程序的性能提升大约一个数量级。第二件事就是实现智能缓存。记住接受的请求,将最频繁的请求文件的数据存储在Hashtable中,使之保存在内存中。使用低优先级的线程更新此缓存。