HTTP服务器可以响应发送错误的信息,并配置可以用文件添加多种请求功能

来源:互联网 发布:mac恢复出厂系统 编辑:程序博客网 时间:2024/05/17 19:20

一.项目根目录下需要的相关文件:

1.配置文件conf下的文件:

(1)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><mime-mapping><extension>png</extension><mime-type>image/png</mime-type></mime-mapping></web-app>

(2)server.xml文件

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

2.访问文件web-apps文件下:

  2.1 myweb文件里的文件:

    2.1.1 web文件里的文件:

    (1)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><a href="login.html">去登陆</a></center></body></html>

(2)bee0.png图片


(3)reg.html文件:注意form表单里的方法为post,则名字密码等信息在请求正文里(隐藏,看不到)
     若方法改为GET则,名字,密码等信息在请求行里

<html><head><title>欢迎注册</title><meta charset="UTF-8"></head><body><center><h1>用户注册</h1><form method="post" action="regUser"><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>

(4)reg_success.html文件

<html><head><title>注册成功</title><meta charset="UTF-8"></head><body><center><h1>注册成功</h1><form method="post" action="../regUser"></center></body></html>

(5)reg_fail.html文件

<html><head><title>注册失败</title><meta charset="UTF-8"></head><body><center><h1>注册失败</h1><form method="post" action="../regUser"><a href="reg.html">重新注册</a></center></body></html>

(6)login.html文件:

<html><head><title>欢迎登陆</title><meta charset="UTF-8"></head><body><center><h1>用户登陆</h1><form method="post" action="loginUser"><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>

(7)login_success.html文件:

<html><head><title>登陆成功</title><meta charset="UTF-8"></head><body><center><h1>登陆成功</h1><form method="post" action="../loginUser"></center></body></html>

(8)login_fail.html文件:

<html><head><title>登陆失败</title><meta charset="UTF-8"></head><body><center><h1>登陆失败</h1><form method="post" action="../loginUser"><a href="login.html">重新登陆</a></center></body></html>

(9)500.html文件

<html><head><meta charset="utf-8"></head><body><center><h1>500</h1><h2>服务器异常!</h2></center></body></html>

    2.1.2 WEB-INF文件里的文件:

    (1)web.xml文件

<?xml version="1.0" encoding="UTF-8"?><web-app><!-- 配置一个Servlet的类名,用于反射 --><servlet><servlet-name>RegServlet</servlet-name><servlet-class>web.v4.RegServlet</servlet-class></servlet><servlet><servlet-name>LoginServlet</servlet-name><servlet-class>web.v4.LoginServlet</servlet-class></servlet><!-- mapping用于将请求与Servlet关联 --><servlet-mapping><servlet-name>RegServlet</servlet-name><url-pattern>/myweb/web/regUser</url-pattern></servlet-mapping><servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/myweb/web/loginUser</url-pattern></servlet-mapping></web-app>

2.2 ROOT文件里的文件:

    (1)404.html文件

<html><head><meta charset="utf-8"></head><body><center><h1>404</h1><h2>没有找到该资源!</h2></center></body></html>

二.建9个类,一个服务器,一个请求解析,一个响应用户,一个服务器配置信息,一个Http协议相关定义信息,
一个处理注册用户的信息的子类,一个处理登陆用户的信息的子类,一个处理用户请求的父类,
一个服务器请求项目配置信息

(1)服务器请求项目配置信息

package web.v4;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.util.HashMap;import java.util.List;import java.util.Map;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;/** * 对应项目WEB-INF目录中web.xml中的配置信息 * @author soft01 * */public class ApplicationConfig {/* * 保存web.xml文件中所有<servlet>中的信息 * key:子标签<servlet-name>中间的文本信息 * value:子标签<servlet-class>中间的文本信息 */private Map<String,String> servletMap = new HashMap<String,String>();/* * 保存web.xml文件中所有<servlet-mapping> * 中的信息 * key:子标签<url-pattern>中间的文本信息 * value:子标签<servlet-name>中间的文本信息 */private Map<String,String> servletMappingMap = new HashMap<String,String>();/** * 根据给定的文件初始化配置对象 * @param file */public ApplicationConfig(File file) {try {SAXReader reader = new SAXReader();Document doc = reader.read(new FileInputStream(file));Element root = doc.getRootElement();List<Element> list1 = root.elements("servlet");for(Element ele : list1) {String key = ele.elementText("servlet-name");String value = ele.elementText("servlet-class");servletMap.put(key,value);}List<Element> list2 = root.elements("servlet-mapping");for(Element ele : list2) {String value = ele.elementText("servlet-name");String key = ele.elementText("url-pattern");servletMappingMap.put(key,value);}} catch (FileNotFoundException | DocumentException e) {e.printStackTrace();}}public Map<String, String> getServletMap() {return servletMap;}public Map<String, String> getServletMappingMap() {return servletMappingMap;}}

(2)服务器配置信息的类

package web.v4;import java.io.File;import java.io.FileInputStream;import java.util.HashMap;import java.util.Map;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;/*** 服务端配置数据* 通过解析conf/server.xml文件得到配置数据* 通过解析WEB-INF目录web.xml文件得到请求配置数据* @author soft01**/public class Server {public static String URIENCODING;public static int PORT;public static String PROTOCOL;public static int MAX_THREADS;/** * 每个项目对应的配置文件web.xml信息 * 当前Map的结构 * Key:web-apps中每个项目的名字(web-apps * 中每个目录的名字作为该项目的名字) * Value:对应该项目中WEB-INFO中web.xml文件的 *  配置信息,一个ApplicationConfig实例 */public static Map<String,ApplicationConfig> appConfigs;static {//初始化服务端主配置信息conf/server.xmlinitServerConfig();//初始化所有web项目配置信息initApplicationConfig();}/** * 初始化WebServer主要配置信息端口, * 线程池信息等。 */private static void initServerConfig() {// 解析server.xml文件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");URIENCODING = 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 (Exception e) {e.printStackTrace();}}/* * 初始化每个项目配置信息 */private static void initApplicationConfig() {appConfigs = new HashMap<String,ApplicationConfig>();/* * 表示web-apps目录 */File root = new File("web-apps");/* * 获取web-apps下的所有应用项目目录 */File[] applications = root.listFiles((f) -> f.isDirectory());/* * 目标,将web-apps中每个应用项目中的 WEB-INF目录里的web.xml初始化为一个 ApplicationConfig对象,并将应用 * 目录的名字做为Key,ApplicationConfig 对象作为value,存入到Server的静态属性: appConfigs当中 *//* * 遍历每个应用目录,获取其中的WEB-INF目录 */for (File application : applications) {File[] webInfo = application.listFiles((f) -> f.getName().equals("WEB-INF"));// 若含有WEB-INF目录if(webInfo.length>0) {//获取其中的web.xml文件File webInfDir = webInfo[0];File[] webXmlFile = webInfDir.listFiles((f) -> f.getName().equals("web.xml"));//是否含有web.xml文件if(webXmlFile.length>0) {//根据该web.xml文件初始化配置对象ApplicationConfig app = new ApplicationConfig(webXmlFile[0]);appConfigs.put("/"+application.getName(), app);}}}}}

(3)Http协议相关定义信息的类

package web.v4;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();}}}

(4)处理用户请求的父类

package web.v4;import java.io.File;/** * 跳转到指定请求资源路径 * @author soft01 * */public class HttpServlet {public void service(HttpRequest request,HttpResponse response) {}public void forward(HttpRequest request,HttpResponse response,String url) {File file = new File("web-apps"+url);response.setStatusCode(HttpContext.STATUS_CODE_OK);response.setContentType(HttpContext.MIME_MAPPING.get("html"));response.setContentLength(file.length());response.setEntity(file);}}

(5)处理注册用户的信息的子类

package web.v4;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;public class RegServlet extends HttpServlet{public void service(HttpRequest request,HttpResponse response) {PrintWriter pw = null;BufferedReader br = null;try {/* * 获取本地user.txt里面的用户信息 */String name = request.getParameter("username");String pwd = request.getParameter("password");System.out.println(name);System.out.println(pwd);/* * 首先检查该用户是否已经注册过,若注册过则跳转到提示页面。 */File userFile = new File("user.txt");boolean have = false;if(userFile.exists()) {br = new BufferedReader(new InputStreamReader(new FileInputStream("user.txt"),"utf-8"));String line = null;while((line=br.readLine())!=null) {String[] data = line.split(",");if(name.equals(data[0])) {have = true;break;}}}if (have) {forward(request,response, File.separator + "myweb" + File.separator+ "web"+ File.separator+ "reg_fail.html");} else {/* * 将用户的注册信息写入服务端本地的一个 * 文件user.txt中 */// 写入文件pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("user.txt", true), "utf-8"));pw.println(name + "," + pwd);pw.flush();/* * 响应用户注册成功页面 */forward(request,response, File.separator + "myweb" + File.separator+ "web"+ File.separator+ "reg_success.html");}}catch(Exception e) {e.printStackTrace();forward(request,response,File.separator + "myweb" + File.separator + "web" + File.separator+ "500.html");}finally {if(br != null){try {br.close();} catch (IOException e) {e.printStackTrace();}}if(pw!=null) {pw.close();}}}}

(6)处理登陆用户的信息的子类

package web.v4;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;public class LoginServlet extends HttpServlet{public void service(HttpRequest request,HttpResponse response) {BufferedReader br = null;try {/* * 获取本地user.txt里面的用户信息 */String name = request.getParameter("username");String pwd = request.getParameter("password");System.out.println(name);System.out.println(pwd);/* * 用户跟密码一致则登陆成功 */File userFile = new File("user.txt");boolean have = false;if(userFile.exists()) {br = new BufferedReader(new InputStreamReader(new FileInputStream("user.txt"),"utf-8"));String line = null;while((line=br.readLine())!=null) {String[] data = line.split(",");if(name.equals(data[0]) && pwd.equals(data[1])) {have = true;break;}}}if (have) {forward(request,response, File.separator + "myweb" + File.separator+ "web"+ File.separator+ "login_success.html");} else {/* * 响应用户登陆失败页面 */forward(request,response, File.separator + "myweb" + File.separator+ "web"+ File.separator+ "login_fail.html");}}catch(Exception e) {e.printStackTrace();forward(request,response,File.separator + "myweb" + File.separator + "web" + File.separator+ "500.html");}finally {if(br != null){try {br.close();} catch (IOException e) {e.printStackTrace();}}}}}

(7)请求解析的类

package web.v4;import java.io.IOException;import java.io.InputStream;import java.net.URLDecoder;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; //请求路径private String requestLine;//参数部分private String queryLine;//保存所有传递过来的参数的Mapprivate Map<String,String> parasMap;public HttpRequest(InputStream in) {parseRequestLine(in);parseHeaders(in);parseEntity(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];parseUrl(this.url);protocol = data[2];} catch (Exception e) {e.printStackTrace();throw e;}}private void parseUrl(String url) {if (url.indexOf("?") != -1) {String[] data = url.split("\\?");requestLine = data[0];queryLine = decodeUrl(data[1]);parseQuery(queryLine);}else {requestLine = url;}}private void parseQuery(String queryLine) {parasMap = new LinkedHashMap<String, String>();String[] querys = queryLine.split("&");for (int i = 0; i < querys.length; i++) {String[] data1 = querys[i].split("=");parasMap.put(data1[0], data1[1]);}}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));}/** * 解析消息正文 */private void parseEntity(InputStream in){/* * 通过消息头中获取正文长度 */if(this.headers.containsKey("Content-Length")){int contentLength = Integer.parseInt(this.headers.get("Content-Length"));String contentType = this.headers.get("Content-Type");//根据Content-Type判断是否为form表单if("application/x-www-form-urlencoded".equals(contentType)){System.out.println("开始处理Post请求中form表单内容!");//通过流读取消息正文中的所有字节try {/* form表单中的数据还是字符串,跟Get请求中 * 地址栏中?右侧内容格式一致 */byte[] data = new byte[contentLength];in.read(data);String line = URLDecoder.decode(new String(data,"ISO8859-1"),Server.URIENCODING);System.out.println(line);parseQuery(line);} catch (Exception e) {e.printStackTrace();}}}}/**  * 获取应用项目名  * @return  */public String getContextPath() {/* * 常见的requestLine格式: * 目标,获取下面地址中[]中间的内容 * http://localhost:8088[/web]/web/regUser * http://localhost:8088/ *  * 解析URL后,request中的requestLine如下: * /web/web/regUser * /web/index.html * /index.html */if(requestLine.length()>1) {//查找第二个"/"的位置int index = requestLine.indexOf("/",1);if(index>0) {return requestLine.substring(0,index);}}return "";}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 "";}private String decodeUrl(String line) {try {String str;System.out.println("解码前的内容:" + line);str = URLDecoder.decode(line, Server.URIENCODING);System.out.println("解码后的内容" + str);return str;} catch (Exception e) {e.printStackTrace();}return line;}public String getRequestLine() {return requestLine;}public String getParameter(String name) {return parasMap.get(name);}}

(8)响应用户的类

package web.v4;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;}}

(9)服务器的类

package web.v4;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());if(checkServletRequest(request)) {executeServlet(request,response);//响应静态资源} else {responseFile(request,response);System.out.println("响应完毕");}response.flush();} catch (Exception e) {e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}/** * 根据request判断该请求是否为请求Servlet * @param request * @return */private boolean checkServletRequest(HttpRequest request) {/* * 1:根据请求获取对应应用项目的配置文件 * 2:根据请求路径查看配置文件中是否有对应的 * Servlet,并将判断结果返回。 *///找到"web"项目的配置文件ApplicationConfig config = Server.appConfigs.get(request.getContextPath());//找到对应性用项目的配置文件if(config !=null) {return config.getServletMappingMap().containsKey(request.getRequestLine());}//返回false表示该请求不是对应Servletreturn false;}/** * 执行Servlet * @param request * @param response */private void executeServlet(HttpRequest request,HttpResponse response) {try {ApplicationConfig config = Server.appConfigs.get(request.getContextPath());//找到请求对应的Servlet的名字String servletName = config.getServletMappingMap().get(request.getRequestLine());String servletClassName = config.getServletMap().get(servletName);ClassLoader loader = ClassLoader.getSystemClassLoader();Class c = loader.loadClass(servletClassName);HttpServlet o = (HttpServlet)c.newInstance();o.service(request, response);}catch(Exception e) {e.printStackTrace();}}/** * 响应一个静态资源(静态页面,图片等) * @param request * @param response */private void responseFile(HttpRequest request,HttpResponse response) {File file = new File("web-apps" + request.getRequestLine());if (file.exists() && file.isFile()) {// 响应用户请求的资源// 设置状态码response.setStatusCode(HttpContext.STATUS_CODE_OK);// 设置响应头相关信息/* * 根据文件名的后缀获取对应的Content-Type的值 */String fileName = file.getName();int index = fileName.lastIndexOf(".") + 1;String extension = fileName.substring(index);String contentType = HttpContext.MIME_MAPPING.get(extension);// 设置Content-Typeresponse.setContentType(contentType);response.setContentLength(file.length());// 设置实体数据response.setEntity(file);} else {//响应404页面File notFoundPage = new File("web-apps"+File.separator+"ROOT"+File.separator+"404.html");response.setStatusCode(HttpContext.STATUS_CODE_NOT_FOUND);response.setContentType("text/html");response.setContentLength(notFoundPage.length());response.setEntity(notFoundPage);}}}}

注意:(1)html里面的input代表输入框的意思,再次强调解析XML文档需要建MavenProject导入dom4j的架包,若MavenProject里下面搜索不到,请到http://maven.aliyun.com.nexus去搜索

(2)各文件里的信息同以前版本有些改动,请注意文件信息跟路径。

阅读全文
0 0
原创粉丝点击