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);}}}
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--
- java多线程HTTP服务器
- socket 多线程http服务器
- 一个Java Socket带多线程的http服务器
- Java自定义多线程服务器
- java多线程web服务器
- java Http服务器
- java socket 服务器 http
- java实现http服务器
- 基于多线程Http服务器的查询天气
- java实现http多线程下载
- java http协议 多线程断点续传
- java模拟多线程http请求
- http项目笔记(多线程版 微型http服务器)
- java的多线程ftp服务器
- socket多线程网络服务器 java
- HTTP 服务器开发(Java)--HTTP请求
- Java实现Http服务器之一
- http 服务器的实现(java)
- 获取View类界面控件的位置
- zip tar
- 山东省第七届ACM大学生程序设计竞赛 A Julyed
- APP设计欣赏发布
- 元素水平垂直居中
- java多线程HTTP服务器
- Contains Duplicate III
- 山东省第七届ACM大学生程序设计竞赛 K —Reversed Words
- 图解Android View的scrollTo(),scrollBy(),getScrollX(), getScrollY()
- Hibernate总结
- 计算机中的原码、反码、补码(上)
- 动态规划之0-1背包问题
- leetcode 16. 3Sum Closest
- 2076 Problem F Quick Brown Fox