HTTP服务器简易版

来源:互联网 发布:衣服品牌查询软件 编辑:程序博客网 时间:2024/05/21 06:59
一.读取网页请求,给网页响应文件,该文件是html类型
建3个类,一个服务器,一个解析请求,一个响应用户


1.请求信息的类:

package web;import java.io.IOException;import java.io.InputStream;import java.util.LinkedHashMap;import java.util.Map;public class HttpRequest {/* * 请求行相关信息 */// 请求方法private String method;// 请求资源路径private String url;// 请求使用的协议版本private String protocol;/* * 消息头相关信息 */private Map<String,String> headers; public HttpRequest(InputStream in) {parseRequestLine(in);parseHeaders(in);}public void parseRequestLine(InputStream in) {try {String line = readLine(in);if(line.length()==0){throw new RuntimeException("无效请求");}String[] data = line.split("\\s");method = data[0];url = data[1];protocol = data[2];} catch (Exception e) {e.printStackTrace();throw e;}}public void parseHeaders(InputStream in) {headers = new LinkedHashMap<String, String>();while (true) {String line = readLine(in);if ("".equals(line)) {break;}String[] data = line.split(":");headers.put(data[0].trim(), data[1].trim());}headers.forEach((k, v) -> System.out.println(k + ":" + v));}public String readLine(InputStream in) {try {StringBuilder builder = new StringBuilder();char c1 = 0, c2 = 0;int d=-1;while ((d=in.read())!=-1) {c2 = (char)d;if (c1 == 13 && c2 == 10) {break;}builder.append(c2);c1 = c2;}return builder.toString().trim();} catch (IOException e) {e.printStackTrace();}return "";}public String getUrl() {return url;}}


2.响应信息的类:

package web;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.util.HashMap;import java.util.Map;public class HttpResponse {//状态代码private int statusCode;// 响应头private Map<String,String> headers;//响应实体private File entity;//输出流private OutputStream out;public HttpResponse(OutputStream out){this.out=out;this.headers = new HashMap<String,String>();}public void flush() {// 发送状态行信息sendResponseStatusLine();// 发送响应头信息sendReResponseHeaders();// 发送响应正文sendReResponseContent();}public void sendResponseStatusLine(){String line = "HTTP/1.1"+" "+statusCode+" "+"OK";println(line);}public void sendReResponseHeaders(){headers.forEach((k,v)->println(k+":"+v));println("");}public void sendReResponseContent(){FileInputStream fis = null;try {fis = new FileInputStream(entity);int len = -1;byte[] data = new byte[1024*10];while((len = fis.read(data))!=-1) {out.write(data, 0, len);}} catch (FileNotFoundException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}finally {if(fis!=null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}public void println(String line) {try {out.write(line.getBytes("ISO8859-1"));out.write(13);//CRout.write(10);//LF}catch(UnsupportedEncodingException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}}public void setContentType(String contentType) {this.headers.put("Content-Type", contentType);}public void setContentLength(long length) {this.headers.put("Content-Length", String.valueOf(length));}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public void setEntity(File entity) {this.entity = entity;}}

3.服务器类:

package web;import java.io.File;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/** * 读取网页发送过来的请求行,消息头信息 * @author soft01 * */public class WebServer {ServerSocket server;Socket socket;public WebServer() {try {System.out.println("初始化客户端...");server = new ServerSocket(8088);System.out.println("客户端初始化完毕!");} catch (IOException e) {e.printStackTrace();}}public void start() {try {while (true) {System.out.println("等待客户端连接...");socket = server.accept();System.out.println("一个客户端已连接!");// 1:请求行 method url protocol// 2:消息头:根据请求资源的不同消息头中的内容也不完全一样。// 消息头中每一个信息都以CRLF结束// CR(13)LF(10) 单独读取到一个CRLF表示消息头全部发送完毕// 3:消息正文ClientHandler handler = new ClientHandler(socket);Thread t = new Thread(handler);t.start();}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {try {WebServer server = new WebServer();server.start();}catch(Exception e) {e.printStackTrace();}}private class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket=socket;}public void run() {try {HttpRequest request = new HttpRequest(socket.getInputStream());HttpResponse response = new HttpResponse(socket.getOutputStream());String url = request.getUrl();System.out.println(url);File file = new File("web-apps" + url);if(file.exists()) {// 设置状态码response.setStatusCode(200);// 设置Content-Typeresponse.setContentType("text/html");response.setContentLength(file.length());// 设置实体数据response.setEntity(file);response.flush();}else {System.out.println("文件不存在!");}} catch (Exception e) {e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}}

二.读取网页请求,给网页响应文件,相关信息用xml配置(需要导入dom4j包)

1.项目根目录下创建文件如下:

(1)conf目录下建server.xml和web.xml文件

server.xml如下:

<?xml version="1.0" encoding="UTF-8"?><server><!-- 配置和客户端连接相关的信息客户端传输数据的字符集服务端口等信息 --><Connector URIEncoding="utf-8" port="8088" protocol="HTTP/1.1"/><!-- 线程池的线程数 --><Executor maxThreads="50"/></server>

web.xml如下:

<?xml version="1.0" encoding="UTF-8"?><web-app version="3.1"><mime-mapping><extension>html</extension><mime-type>text/html</mime-type></mime-mapping><mime-mapping><extension>jpg</extension><mime-type>image/jpeg</mime-type></mime-mapping><mime-mapping><extension>jpgm</extension><mime-type>video/jpm</mime-type></mime-mapping><mime-mapping><extension>7z</extension><mime-type>application/x-7z-compressed</mime-type></mime-mapping></web-app>

(2)web-apps目录下建myweb目录,myweb里面的文件建index.html和reg.html和图片bee0.png

index.html如下:

<html><head><title>hello</title><meta charset="UTF-8"></head><body><center><h1>这是我的第一个页面</h1><img src="bee0.png"><a href="reg.html">去注册</a></center></body></html>

reg.html如下:

<html><head><title>欢迎注册</title><meta charset="UTF-8"></head><body><center><h1>用户注册</h1><table border="1"><tr><td>用户名:</td><td><input name ="username" type="text" size=30></td></tr><tr><td>密 码:</td><td><input name ="password" type="password" size=30></td></tr><tr><td colspan="2" align="center"><input type="submit" value="注册"></td></tr></table></center></body></html>

2.建5个类,一个服务器,一个请求解析,一个响应用户,一个服务器配置信息,一个Http协议相关定义信息

(1)服务器配置信息解析server.xml

package web.v2;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;/*** 服务端配置数据* 通过解析conf/server.xml文件得到配置数据* @author soft01**/public class Server {public static String URIENCODEING;public static int PORT;public static String PROTOCOL;public static int MAX_THREADS;static {try {SAXReader reader = new SAXReader();Document doc = reader.read(new FileInputStream("conf"+File.separator+"server.xml"));Element root = doc.getRootElement();Element ele1 = root.element("Connector");URIENCODEING = ele1.attributeValue("URIEncoding");PORT = Integer.parseInt(ele1.attributeValue("port"));PROTOCOL =ele1.attributeValue("protocol");Element ele2 = root.element("Executor");MAX_THREADS = Integer.parseInt(ele2.attributeValue("maxThreads"));} catch (FileNotFoundException e) {e.printStackTrace();} catch (DocumentException e) {e.printStackTrace();}}}

(2)Http协议相关定义信息解析web.xml
package web.v2;import java.io.File;import java.io.FileInputStream;import java.util.HashMap;import java.util.List;import java.util.Map;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;/** * Http协议相关定义信息 * @author soft01 * */public class HttpContext {public static final int CR = 13;public static final int LF = 10;public static final int STATUS_CODE_OK = 200;public static final int STATUS_CODE_NOT_FOUND = 404;public static final int STATUS_CODE_ERROR = 500;public static final Map<Integer,String> STATUS_CODE_MAPPING = new HashMap<Integer,String>();public static final Map<String,String> MIME_MAPPING = new HashMap<String,String>();static {initCodeMapping();initMimeMapping();}private static void initCodeMapping() {STATUS_CODE_MAPPING.put(200, "ok");STATUS_CODE_MAPPING.put(404, "not found");STATUS_CODE_MAPPING.put(500, "internal server error");}private static void initMimeMapping() {/* * 解析当前目录下的子目录conf中的文件web.xml * 将该文件中所有<mime-mapping>标签中内容 * 存入MIME_MAPPING这个Map中 * 其中Key为<extension>标签中的文本信息 * value为<mime-type>标签中的文本信息 */try {SAXReader reader = new SAXReader();Document doc = reader.read(new FileInputStream("conf"+File.separator+"web.xml"));Element root = doc.getRootElement();List<Element> eles = root.elements("mime-mapping");for(Element ele : eles) {MIME_MAPPING.put(ele.elementText("extension"), ele.elementText("mime-type"));}}catch(Exception e) {e.printStackTrace();}}}

(3)请求解析

package web.v2;import java.io.IOException;import java.io.InputStream;import java.util.LinkedHashMap;import java.util.Map;public class HttpRequest {/* * 请求行相关信息 */// 请求方法private String method;// 请求资源路径private String url;// 请求使用的协议版本private String protocol;/* * 消息头相关信息 */private Map<String,String> headers; public HttpRequest(InputStream in) {parseRequestLine(in);parseHeaders(in);}public void parseRequestLine(InputStream in) {try {String line = readLine(in);if(line.length()==0){throw new RuntimeException("无效请求");}String[] data = line.split("\\s");method = data[0];url = data[1];protocol = data[2];} catch (Exception e) {e.printStackTrace();throw e;}}public void parseHeaders(InputStream in) {headers = new LinkedHashMap<String, String>();while (true) {String line = readLine(in);if ("".equals(line)) {break;}String[] data = line.split(":");headers.put(data[0].trim(), data[1].trim());}headers.forEach((k, v) -> System.out.println(k + ":" + v));}public String readLine(InputStream in) {try {StringBuilder builder = new StringBuilder();char c1 = 0, c2 = 0;int d=-1;while ((d=in.read())!=-1) {c2 = (char)d;if (c1 == HttpContext.CR && c2 == HttpContext.LF) {break;}builder.append(c2);c1 = c2;}return builder.toString().trim();} catch (IOException e) {e.printStackTrace();}return "";}public String getUrl() {return url;}}

(4)响应用户

package web.v2;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.util.HashMap;import java.util.Map;public class HttpResponse {//状态代码private int statusCode;// 响应头private Map<String,String> headers;//响应实体private File entity;//输出流private OutputStream out;public HttpResponse(OutputStream out){this.out=out;this.headers = new HashMap<String,String>();}public void flush() {// 发送状态行信息sendResponseStatusLine();// 发送响应头信息sendReResponseHeaders();// 发送响应正文sendReResponseContent();}public void sendResponseStatusLine(){String line = Server.PROTOCOL+" "+statusCode+" "+HttpContext.STATUS_CODE_MAPPING.get(statusCode);println(line);}public void sendReResponseHeaders(){headers.forEach((k,v)->println(k+":"+v));println("");}public void sendReResponseContent(){FileInputStream fis = null;try {fis = new FileInputStream(entity);int len = -1;byte[] data = new byte[1024*10];while((len = fis.read(data))!=-1) {out.write(data, 0, len);}} catch (FileNotFoundException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}finally {if(fis!=null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}public void println(String line) {try {out.write(line.getBytes("ISO8859-1"));out.write(HttpContext.CR);//CRout.write(HttpContext.LF);//LF}catch(UnsupportedEncodingException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}}public void setContentType(String contentType) {this.headers.put("Content-Type", contentType);}public void setContentLength(long length) {this.headers.put("Content-Length", String.valueOf(length));}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public void setEntity(File entity) {this.entity = entity;}}

(5)服务器

package web.v2;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;/** * 读取网页发送过来的请求行,消息头信息 * @author soft01 * */public class WebServer {ServerSocket server;Socket socket;private ExecutorService threadPool;public WebServer() {try {System.out.println("初始化客户端...");server = new ServerSocket(Server.PORT);threadPool = Executors.newFixedThreadPool(Server.MAX_THREADS);System.out.println("客户端初始化完毕!");} catch (IOException e) {e.printStackTrace();}}public void start() {try {while (true) {System.out.println("等待客户端连接...");socket = server.accept();System.out.println("一个客户端已连接!");// 1:请求行 method url protocol// 2:消息头:根据请求资源的不同消息头中的内容也不完全一样。// 消息头中每一个信息都以CRLF结束// CR(13)LF(10) 单独读取到一个CRLF表示消息头全部发送完毕// 3:消息正文ClientHandler handler = new ClientHandler(socket);threadPool.execute(handler);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {try {WebServer server = new WebServer();server.start();}catch(Exception e) {e.printStackTrace();}}private class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket=socket;}public void run() {try {HttpRequest request = new HttpRequest(socket.getInputStream());HttpResponse response = new HttpResponse(socket.getOutputStream());String url = request.getUrl();File file = new File("web-apps" + url);if(file.exists()) {// 设置状态码response.setStatusCode(HttpContext.STATUS_CODE_OK);// 设置Content-TypeString fileName = file.getName();int index = fileName.lastIndexOf(".")+1;String extension = fileName.substring(index);String contentType = HttpContext.MIME_MAPPING.get(extension);response.setContentType(contentType);response.setContentLength(file.length());// 设置实体数据response.setEntity(file);response.flush();}else {System.out.println("文件不存在!");}} catch (Exception e) {e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}}