java多线程HTTP服务器

来源:互联网 发布:仿斗鱼直播源码 编辑:程序博客网 时间:2024/04/28 23:30

       基于java线程池、java Socket实现了一个简单的多线程Http服务器,可以实现GET资源获取功能、GET表单提交功能,POST  表单提交功能、单文件上传功能。

   源码可以在此下载:http://download.csdn.net/detail/luckydog1991/9545387

   注:1.要运行程序,需要指定服务器运行根目录,并将服务资源放入根目录

           2.程序运行参数:需要指定服务根目录路径,如 H:\MyHttpRoot ,以及端口号 如 8888,如未指定,则默认80,有可能会冲突。 运行MyHttpServer.java文件,指定参数如 : H:\MyHttpRoot 8888    

          3.运行程序后,在浏览器中输入 http://localhost:端口号/资源路径即可。例如绑定的是8888端口,在服务器运行根目录下有index.html文件,现在需要访问这个文件,   在浏览器中输入:http://localhost:8888/   或 http://localhost:8888/index.html 即可访问。如果绑定的是80端口,可直接写:http://localhost/index.html,浏览器默认使用80作为服务器端口。


<head> <title>Test my Httpserver</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body> <p>Test get</p> <form name="testGet" method="get" action="http://localhost:8888/"><input type="text" name="filename" />输入文件名<br><select name="myage">  <option value="18">18</option>  <option value="20">20</option>  <option value="22">22</option></select><br><input type="submit"value="Sutmit"></form><p>Test post</p> <form name="testPost" method="post" action="http://localhost:8888/">名字<input type="text" name="myname" /><br>年龄<select name="myage">  <option value="18">18</option>  <option value="20">20</option>  <option value="22">22</option></select><br><input type="submit"value="Sutmit"></form><p>Test upload file</p> <form action='http://localhost:8888/' method='post' enctype='multipart/form-data'>    file: <input type='file' name='myfile' /><br>    <input type='submit' />    </form></body> </html>

MyHttpServer.java如下:

package com.xl;import java.io.File;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class MyHttpServer {private static final int NUM_THREADS = 50 ; //线程池线程数量private static final  String INDEX_FILE = "index.html" ;  //服务器首页索引文件名private final File  rootDirectory ;  //服务器根目录,服务器资源都放在该目录下private final int port = 80 ;  //服务器端口号   默认设置为80public MyHttpServer(File rootDirectory , int port) throws IOException{if(!rootDirectory.isDirectory()){throw new IOException(rootDirectory + "is not a directory");}this.rootDirectory = rootDirectory ;this.port = port ;}public void start() throws IOException{  //启动服务器ExecutorService  pool = Executors.newFixedThreadPool(NUM_THREADS); //服务器工作线程池try(ServerSocket server = new ServerSocket(port)){while(true){try{Socket request = server.accept();  //接受请求,后提交线程池处理Runnable r = new ProcessorRequest(rootDirectory,INDEX_FILE,request);pool.submit(r);}catch(IOException ex){System.out.println(" Error accepting connect"+ ex);}}}}public static void main(String[] args) {   //服务器主函数,File  docRoot ;try{docRoot = new File(args[0]);  //解析参数,确定服务器根目录if(!docRoot.isDirectory()){System.out.println("Error , docRoot is not a directory");return ;}}catch(ArrayIndexOutOfBoundsException ex){System.out.println("Please input docRoot name");return;}int port ;try{port = Integer.parseInt(args[1]); //解析参数 ,获取端口号}catch(RuntimeException e){port = 80 ;}try{MyHttpServer httpServer = new MyHttpServer(docRoot, port);httpServer.start();}catch(IOException e){System.out.println("Can not start Server"+ e);}}}


ProcessorRequest.java 文件如下:

package com.xl;import java.io.BufferedOutputStream;import java.io.DataInputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.Writer;import java.net.Socket;import java.net.URLConnection;import java.nio.file.Files;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.Map.Entry;import java.util.Set;public class ProcessorRequest  implements Runnable {private File rootDirectory ;private String indexFileNname = "index.html" ;private Socket  connection;public ProcessorRequest(File directory ,String n ,Socket s){if(directory != null){this.rootDirectory = directory ;}if(n != null ){this.indexFileNname = n ;}this.connection = s ;}@Overridepublic void run() {// TODO Auto-generated method stubtry{OutputStream rawOut = new BufferedOutputStream(connection.getOutputStream());DataInputStream in = new DataInputStream(connection.getInputStream()) ;StringBuilder requestLine = new StringBuilder();  //获得请求行while(true){char c = (char) in.read();if(c == '\r' || c == '\n' ||c == -1){break;}requestLine.append(c);}String reqLine = requestLine.toString();String [] tokens = reqLine.split("\\s+");String method = tokens[0];if(method.equalsIgnoreCase("GET")){ //GET请求处理doGet(in , rawOut , reqLine);}else if(method.equalsIgnoreCase("POST")){ //POST请求处理doPost(in , rawOut , reqLine);}else{                           //其他请求,暂不处理,返回 <span style="font-family: Arial, Helvetica, sans-serif;">501</span>String fileName = tokens[1];String version = null ;if(tokens.length > 2){version = tokens[2];}Writer out = new OutputStreamWriter(rawOut);String body = new StringBuilder("HTTP error 501 :Not Implemented \r\n").toString();if(version.startsWith("HTTP/")){senderHeader(out,"Http/1.0 501 Not Implemented ","text/html;charset=utf-8",body.length());}out.write(body);out.flush();}}catch(Exception ex){System.out.println("Error of "+connection.getRemoteSocketAddress()+ex);}finally{try{connection.close();}catch(IOException E){}}}
    //处理Get请求,从服务器根目录寻找资源,并返回。如果提交的get请求有url参数,则转化参数Map对象,有待进一步根据需要处理。private void doGet(DataInputStream in , OutputStream out, String reqLine) throws IOException{String [] tokens = reqLine.split("\\s+");String filePath = tokens[1];String fileName = filePath;if(filePath.indexOf('?') != -1){String fileNm = fileName.substring(0,fileName.indexOf('?'));String parameters = fileName.substring(fileName.indexOf('?')+1, fileName.length());String [] pars = parameters.split("&");HashMap<String ,ArrayList<String>> parameterMap = new HashMap<String ,ArrayList<String>>();for(String s : pars){String[] kv = s.split("=");String key = null;String value = null;if(kv.length == 2 ){key = kv[0] ;value = kv[1];}else if(kv.length == 1 ){key = kv[0] ;value = "";}else{continue ;}ArrayList<String> values = parameterMap.get(key);if(values == null){values = new ArrayList<String>();values.add(value);parameterMap.put(key, values);}else{values.add(value);}}fileName = fileNm;doGetWithParameter( in ,  out, fileName , parameterMap);    return ;}else{if(fileName.endsWith("/")){fileName += indexFileNname;}}String contentTpye = URLConnection.getFileNameMap().getContentTypeFor(fileName);//根据请求资源名,查询资源类型String version = null ;if(tokens.length > 2){version = tokens[2];}Writer outPut = new OutputStreamWriter(out);File theFile = new File(rootDirectory,fileName.substring(1, fileName.length()));if(theFile.canRead() && theFile.getCanonicalPath().startsWith(rootDirectory.getPath())&& !theFile.isDirectory()){ //quebaobyte[] theData = Files.readAllBytes(theFile.toPath());if(version.startsWith("HTTP/")){senderHeader(outPut,"Http/1.0 200 OK",contentTpye,theData.length);}out.write(theData);out.flush();}else{String body = new StringBuilder("HTTP error 404 :File Not Found\r\n").toString();if(version.startsWith("HTTP/")){senderHeader(outPut,"Http/1.0 404 File Not Found","text/html;charset=utf-8",body.length());}outPut.write(body);outPut.flush();}}void doGetWithParameter( DataInputStream in , OutputStream out, String fileName ,HashMap<String ,ArrayList<String>> parameterMap) throws IOException{   }

//处理Post请求。如果Content-Type 是x-www-form-urlencoded,则转化参数Map对象,有待进一步根据需要处理。如果是文件上传(暂时只支持单文件)将文件保存到根目录。

private void doPost(DataInputStream in , OutputStream out, String reqLine) throws IOException{String [] tokens = reqLine.split("\\s+");String reqPath = tokens[1] ;HashMap<String ,String> headers = new HashMap<String ,String>();in.skip(1);//StringBuilder line = new StringBuilder();//while(true){//char c = (char) in.read();//if(c == '\r' || c == '\n' ){//break;//}//line.append(c);//}//String theLine = line.toString();String theLine = in.readLine();        while (theLine != null) {            System.out.println(theLine);                     if ("".equals(theLine)) {                break;            }            String [] headKV = theLine.split(": ");            headers.put(headKV[0], headKV[1]);            theLine = in.readLine();        }       Set<Entry<String, String>>entrys = headers.entrySet();for(Entry<String, String> h : entrys){if(h.getKey().equalsIgnoreCase("Content-Type")){if(h.getValue().contains("application/x-www-form-urlencoded")){doPostWithformUrlencoded( in ,  out , headers  );return ;}else if(h.getValue().contains("multipart/form-data")){doPoatWithMultiPart( in ,  out , headers );return ;}}}Writer outPut = new OutputStreamWriter(out);String body = new StringBuilder("HTTP error 501 :Not Implemented \r\n").toString();String version = null ;if(tokens.length > 2){version = tokens[2];}if(version.startsWith("HTTP/")){senderHeader(outPut,"Http/1.0 501 Not Implemented ","text/html;charset=utf-8",body.length());}outPut.write(body);outPut.flush();}void doPostWithformUrlencoded(DataInputStream in , OutputStream out ,HashMap<String ,String> headers ) throws IOException{Writer outPut = new OutputStreamWriter(out);int contentLength = 0 ; Set<Entry<String, String>>entrys = headers.entrySet();for(Entry<String, String> h : entrys){if(h.getKey().equalsIgnoreCase("Content-Length")){contentLength =  Integer.parseInt(h.getValue());break ;}}if(contentLength != 0){byte []bodyContent = new byte[contentLength];int totalRed = 0 ;int size = 0 ;while(totalRed < contentLength){  size = in.read(bodyContent, totalRed, contentLength-totalRed) ;  totalRed += size;}String parameters = new String(bodyContent);String [] pars = parameters.split("&");HashMap<String ,ArrayList<String>> parameterMap = new HashMap<String ,ArrayList<String>>();for(String s : pars){String[] kv = s.split("=");String key = null;String value = null;if(kv.length == 2 ){key = kv[0] ;value = kv[1];}else if(kv.length == 1 ){key = kv[0] ;value = "";}else{continue ;}ArrayList<String> values = parameterMap.get(key);if(values == null){values = new ArrayList<String>();values.add(value);parameterMap.put(key, values);}else{values.add(value);}}StringBuilder body = new StringBuilder();body.append("<html><head><title>Test post with formUrlencoded</title></head><body><p>Post is ok</p></body></html>");senderHeader(outPut,"Http/1.0 200 OK","text/html;charset=utf-8",body.length());outPut.write(body.toString());outPut.flush();}}    void doPoatWithMultiPart(DataInputStream in , OutputStream outPut,HashMap<String ,String> headers  ) throws IOException{    int contentLength = 0 ;    String  boundary = null; Set<Entry<String, String>>entrys = headers.entrySet();for(Entry<String, String> h : entrys){if(h.getKey().equalsIgnoreCase("Content-Length")){contentLength =  Integer.parseInt(h.getValue());}if(h.getKey().equalsIgnoreCase("Content-Type")){boundary = h.getValue().substring(h.getValue().indexOf("boundary=")+9, h.getValue().length());}} if (contentLength != 0) {                      byte[] buf = new byte[contentLength];            int totalRead = 0;            int size = 0;            while (totalRead < contentLength) {                size = in.read(buf, totalRead, contentLength - totalRead);                totalRead += size;            }                       String dataString = new String(buf, 0, totalRead);            System.out.println("the data user posted:/n" + dataString);            int pos = dataString.indexOf(boundary);                      pos = dataString.indexOf("\n", pos) + 1;            pos = dataString.indexOf("\n", pos) + 1;            pos = dataString.indexOf("\n", pos) + 1;            pos = dataString.indexOf("\n", pos) + 1;                      int start = dataString.substring(0, pos).getBytes().length;            pos = dataString.indexOf(boundary, pos) - 4;                       int end = dataString.substring(0, pos).getBytes().length;                        int fileNameBegin = dataString.indexOf("filename") + 10;            int fileNameEnd = dataString.indexOf("\n", fileNameBegin);            String fileName = dataString.substring(fileNameBegin, fileNameEnd);                      if(fileName.lastIndexOf("//")!=-1){                fileName = fileName.substring(fileName.lastIndexOf("//") + 1);            }            fileName = fileName.substring(0, fileName.length()-2);            OutputStream fileOut = new FileOutputStream(new File(rootDirectory,  fileName));            fileOut.write(buf, start, end-start);            fileOut.close();                                 String body ="<html><head><title>Test upload </title></head><body><p>Post upload is ok</p></body></html>";Writer writer = new OutputStreamWriter(outPut);senderHeader(writer,"Http/1.0 200 OK","text/html;charset=utf-8",body.length());writer.write(body.toString());writer.flush();        }      }        void senderHeader(Writer out ,String responseCode  ,String contentType ,int length) throws IOException{out.write(responseCode+"\r\n");Date  now = new Date();out.write("Date: " + now +"\r\n");out.write("Server: MyHTTP 1.0\r\n");out.write("Content-length: "+length+"\r\n");out.write("Content-type: "+contentType+"\r\n\r\n");out.flush();}}

附:上传文件时的post请求头以及请求体:

POST /   HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 271
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://localhost:8888
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4bDofqqxBk5vWRr1
Referer: http://localhost:8888/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8


------WebKitFormBoundary4bDofqqxBk5vWRr1
Content-Disposition: form-data; name="myfile"; filename="新建文本文档.txt"
Content-Type: text/plain


szprinter 0x0045fcdc "HP LaserJet Pro MFP M225-M226 PCL 6,winspool,Ne04:"
------WebKitFormBoundary4bDofqqxBk5vWRr1--


0 0
原创粉丝点击