package jHttpNew; import java.net.*; import java.io.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class JHTTP extends Thread { //文件所在目录 private File documentRootDirectory; //默认的文件名 private String indexFileName = "index.html"; private ServerSocket server; public JHTTP(File documentRootDirectory, int port, String indexFileName) throws IOException { if (!documentRootDirectory.isDirectory()) { throw new IOException(documentRootDirectory + " does not exist as a directory"); } this.documentRootDirectory = documentRootDirectory; this.indexFileName = indexFileName; this.server = new ServerSocket(port); } public JHTTP(File documentRootDirectory, int port) throws IOException { this(documentRootDirectory, port, "index.html"); } public JHTTP(File documentRootDirectory) throws IOException { this(documentRootDirectory, 80, "index.html"); } public void run() { //建立一个Http Request线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,TimeUnit.SECONDS, new ArrayBlockingQueue(3),new ThreadPoolExecutor.AbortPolicy()); System.out.println("Accepting connections on port " + server.getLocalPort()); System.out.println("Document Root: " + documentRootDirectory); while (true) { try { //使用服务器端socket,接受http请求 Socket request = server.accept(); //用线程池处理http请求 threadPool.execute(new ThreadPoolTask(documentRootDirectory, indexFileName, request)); } catch (IOException ex) { } } } public static void main(String[] args) { // get the Document root File docroot; try { docroot = new File("C:/jhttp_test"); } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("Usage: java JHTTP docroot port indexfile"); return; } // set the port to listen on int port; try { port = Integer.parseInt(args[1]); if (port < 0 || port > 65535) port = 80; } catch (Exception ex) { port = 80; } try { JHTTP webserver = new JHTTP(docroot, port); webserver.start(); } catch(IOException ex) { System.out.println("Server could not start because of an " + ex.getClass()); System.out.println(ex); } } }
package jHttpNew; 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.Serializable; import java.io.Writer; import java.net.Socket; import java.util.Date; import java.util.StringTokenizer; public class ThreadPoolTask implements Runnable, Serializable { private static final long serialVersionUID = 0; private File documentRootDirectory; private String indexFileName = "index.html"; private Object threadPoolTaskData; ThreadPoolTask(File documentRootDirectory, String indexFileName, Object tasks) { if (documentRootDirectory.isFile()) { throw new IllegalArgumentException("documentRootDirectory must be a directory, not a file"); } this.documentRootDirectory = documentRootDirectory; try { this.documentRootDirectory = documentRootDirectory.getCanonicalFile(); } catch (IOException ex) { } if (indexFileName != null) this.indexFileName = indexFileName; this.threadPoolTaskData = tasks; } public void run() { // for security checks String root = documentRootDirectory.getPath(); // 建立socket connection对象 Socket connection; connection = (Socket) threadPoolTaskData; 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 requestLine = new StringBuffer(); //读取输入流,这里只读第一行 int c; while (true) { c = in.read(); if (c == '\r' || c == '\n') break; requestLine.append((char) c); } String get = requestLine.toString(); // log the request System.out.println(get); StringTokenizer st = new StringTokenizer(get); String method = st.nextToken(); String version = ""; if (method.equals("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() // Don't let clients outside the document root && 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 ")) { // send a MIME header 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(); } // end if // send the file; it may be an image or other binary data // so use the underlying output stream // instead of the writer raw.write(theData); raw.flush(); } // end if //如果文件不存在,返回404 else { // can't find the file if (version.startsWith("HTTP ")) { // send a MIME header 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("\r\n"); out.write("File Not Found \r\n"); out.write("\r\n"); out.write(""); out.write("HTTP Error 404: File Not Found
\r\n"); out.write("\r\n"); out.flush(); } } //这里只是先实现了GET方法,如果用户没有输入GET,那么返回501的返回码,表示方法未实现 else { // method does not equal "GET" if (version.startsWith("HTTP ")) { // send a MIME header 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("\r\n"); out.write("Not Implemented \r\n"); out.write("\r\n"); out.write(""); out.write("HTTP Error 501: Not Implemented
\r\n"); out.write("\r\n"); out.flush(); } } catch (IOException ex) { } finally { try { connection.close(); } catch (IOException ex) {} } } // end run 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"; } }
以上只简单实现了GET方法,去获取服务器某个文件路径下的某个文件。这个http服务器的功能可以扩展到POST和DELETE方法,我们可以自行添加。
在Eclipse里面运行JHTTP类,启动这个服务器,控制台显示信息如下:

在本机上用命令行做试验:

回车后在命令行输入:
GET /index.html
就会显示服务器指定路径下的index.html文件的内容
参考资料:
《java Network Programming》 Elliotte Rusty Harold 著