java多线程HTTP服务器

       基于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 ;  //服务器端口号   默认设置为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<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--


  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计一个Java线程Web服务器大致需要以下步骤: 1. 创建一个ServerSocket对象并绑定端口号,等待客户端连接。 2. 当有客户端连接时,创建一个新的线程来处理该客户端的请求。 3. 在新线程中使用Socket对象获取输入流和输出流,接收客户端的HTTP请求,并返回HTTP响应。 4. 解析HTTP请求,获取请求的URL和请求方法等信息。 5. 根据请求的URL和请求方法,调用相应的处理方法来处理请求。 6. 处理方法可以是静态资源的访问,动态资源的访问或请求的转发等。 7. 处理完请求后,关闭Socket连接。 8. 循环执行步骤2-7,等待下一个客户端连接。 实现一个Java线程Web服务器的具体步骤如下: 1. 创建一个ServerSocket对象并绑定端口号,等待客户端连接。 ``` ServerSocket serverSocket = new ServerSocket(port); while (true) { Socket socket = serverSocket.accept(); new Thread(new RequestHandler(socket)).start(); } ``` 2. 创建一个RequestHandler类来处理客户端的请求。 ``` public class RequestHandler implements Runnable { private Socket socket; public RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); // 解析HTTP请求,获取请求的URL和请求方法等信息 String request = parseRequest(inputStream); String[] requestLine = request.split(" "); String method = requestLine[0]; String url = requestLine[1]; // 根据请求的URL和请求方法,调用相应的处理方法来处理请求 String response = processRequest(method, url); // 返回HTTP响应 outputStream.write(response.getBytes()); outputStream.flush(); // 关闭Socket连接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 3. 在RequestHandler类中实现parseRequest方法,解析HTTP请求。 ``` private String parseRequest(InputStream inputStream) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder request = new StringBuilder(); String line; while ((line = reader.readLine()) != null && !line.isEmpty()) { request.append(line).append("\r\n"); } return request.toString(); } ``` 4. 在RequestHandler类中实现processRequest方法,根据请求的URL和请求方法,调用相应的处理方法来处理请求。 ``` private String processRequest(String method, String url) { if ("GET".equals(method)) { if ("/".equals(url)) { // 处理静态资源的访问 return serveFile("index.html"); } else if ("/hello".equals(url)) { // 处理动态资源的访问 return "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, world!"; } else { // 请求的转发 return forwardRequest(url); } } else { return "HTTP/1.1 405 Method Not Allowed\r\n\r\n"; } } ``` 5. 在RequestHandler类中实现serveFile方法,处理静态资源的访问。 ``` private String serveFile(String fileName) { try { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName); byte[] data = new byte[inputStream.available()]; inputStream.read(data); String contentType = getContentType(fileName); return "HTTP/1.1 200 OK\r\nContent-Type: " + contentType + "\r\n\r\n" + new String(data); } catch (IOException e) { return "HTTP/1.1 404 Not Found\r\n\r\n"; } } ``` 6. 在RequestHandler类中实现forwardRequest方法,请求的转发。 ``` private String forwardRequest(String url) { try { URL forwardUrl = new URL("http://localhost:8080" + url); HttpURLConnection connection = (HttpURLConnection) forwardUrl.openConnection(); connection.setRequestMethod("GET"); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line).append("\r\n"); } return response.toString(); } catch (IOException e) { return "HTTP/1.1 404 Not Found\r\n\r\n"; } } ``` 7. 在RequestHandler类中实现getContentType方法,根据文件名获取相应的Content-Type。 ``` private String getContentType(String fileName) { if (fileName.endsWith(".html") || fileName.endsWith(".htm")) { return "text/html"; } else if (fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")) { return "image/jpeg"; } else if (fileName.endsWith(".gif")) { return "image/gif"; } else if (fileName.endsWith(".png")) { return "image/png"; } else if (fileName.endsWith(".js")) { return "application/javascript"; } else if (fileName.endsWith(".css")) { return "text/css"; } else { return "application/octet-stream"; } } ``` 这样就完成了一个简单的Java线程Web服务器的设计与实现。需要注意的是,这只是一个简单的Demo,实际生产环境中需要考虑更多的安全性、可靠性和性能等方面的因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值