java多线程HTTP服务器
2016-06-09 20:52
531 查看
基于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作为服务器端口。
MyHttpServer.java如下:
ProcessorRequest.java 文件如下:
//处理Post请求。如果Content-Type 是x-www-form-urlencoded,则转化参数Map对象,有待进一步根据需要处理。如果是文件上传(暂时只支持单文件)将文件保存到根目录。
附:上传文件时的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--
源码可以在此下载: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 ; //服务器端口号 默认设置为80 public 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 ; } @Override public void run() { // TODO Auto-generated method stub try{ 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< 4000 ;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()){ //quebao byte[] 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--
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序