您的位置:首页 > 理论基础 > 计算机网络

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作为服务器端口。

<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--
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息