相信每一个java程序猿在学习javaWeb的时候,或多或少接触了Servlet,或者说通过Servlet来完成页面发送的请求。 今天,模仿Servlet接受和处理请求实现一个简单的httpServer。该Server能处理简单的表单提交请求。 一、首先建立一个maven工程,目录如图:
二、核心组件类 1.启动类Server.java
package com.httpServer.server; import java.io.IOException; import java.net.ServerSocket; /** * 服务器类 启动服务 */ public class Server { private ServerSocket serverSocket; private boolean isShutDown = false; public static void main(String[] args){ Server server = new Server(); server.start(8080); } private void start(int port) { try { serverSocket = new ServerSocket(8080); receive(serverSocket); } catch (IOException e) { e.printStackTrace(); } } private void receive(ServerSocket serverSocket) { while(! isShutDown) { //接受多个客户端的请求 try { new Thread(new Dispatcher(serverSocket.accept())).start(); //对于每个客户端的请求都生成一个线程处理 } catch (IOException e) { stop(); } } } private void stop(){ try { serverSocket.close(); isShutDown = true; } catch (IOException e) { e.printStackTrace(); } } }2,处理客户端请求的线程类
package com.httpServer.server; import java.io.IOException; import java.net.Socket; public class Dispatcher implements Runnable{ private int code; private Request request; private Response response; public Dispatcher(Socket socket){ try { request = new Request(socket.getInputStream()); response = new Response(socket.getOutputStream()); code = 200; } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { try { Servlet servlet = WebApp.getServlet(request.getUrl()); servlet.service(request, response); //调用对应servlet 的service方法 response.pushToClient(code); } catch (Exception e) { code = 404; e.printStackTrace(); } } }3.Request.java
package com.httpServer.server; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Request { private String method; //请求方式 private String url; //请求路径 private Map<String, List<String>> paramsMap; //请求参数 值可以是多个,如兴趣爱好 private String requestInfo; private String CRLF = "\r\n"; public Request(InputStream is){ requestInfo = new String(); byte[] msg = new byte[10*1024]; try { int len = is.read(msg); if(len != -1) requestInfo = new String(msg, 0, len).trim(); } catch (IOException e) { e.printStackTrace(); } parseRequestInfo(); } public String[] getParameterValues(String name){ if(paramsMap == null){ return null; } return paramsMap.get(name).toArray(new String[0]); } public String getParameter(String name){ String[] arr = getParameterValues(name); if(arr == null){ return null; } return arr[0]; } //解析请求头 private void parseRequestInfo(){ { if(requestInfo == null || "".equals(requestInfo.trim())){ return; } System.out.println(requestInfo); String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF)); int idx = firstLine.indexOf("/"); this.method = firstLine.substring(0, idx).trim(); String urlStr = firstLine.substring(idx, firstLine.indexOf("HTTP/")).trim(); if("POST".equalsIgnoreCase(method)){ this.url = urlStr; String paramStr = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim(); this.paramsMap = parseParams(paramStr); }else if("GET".equalsIgnoreCase(method)){ if(urlStr.contains("?")){ String[] arr = urlStr.split("\\?"); this.url = arr[0]; String paramStr = arr[1]; this.paramsMap = parseParams(paramStr); }else{ this.url = urlStr; } } } } /** * 解析请求参数 * @return */ private Map<String, List<String>> parseParams(String paramStr){ Map<String, List<String>> paramMap = new HashMap<String, List<String>>(); if(paramStr.contains("&")){ String[] paramsArr = paramStr.split("&"); for(int i = 0; i < paramsArr.length; i++){ String str = paramsArr[i]; if(str.contains("=")){ String[] paramArr = str.split("="); if(paramArr.length == 2){ if(paramMap.containsKey(paramArr[0])){ List<String> list = paramMap.get(paramArr[0]); list.add(decode(paramArr[1], "utf-8")); paramMap.put(paramArr[0], list); }else{ List<String> list = new ArrayList<String>(); list.add(decode(paramArr[1], "utf-8")); paramMap.put(paramArr[0], list); } }else{ //uname=&pass=123 if(paramMap.containsKey(paramArr[0])){ List<String> list = paramMap.get(paramArr[0]); list.add(null); paramMap.put(paramArr[0], list); }else{ List<String> list = new ArrayList<String>(); list.add(null); paramMap.put(paramArr[0], list); } } } } } return paramMap; } //对前端传入的中文解码 private String decode(String name, String code){ try { return java.net.URLDecoder.decode(name, code); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } public String getUrl() { return url; } }4.Response.java
package com.httpServer.server; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Date; public class Response { private StringBuffer headInfo; private StringBuilder content; private BufferedWriter bw; private int code; private int len; private final String BLANK = " "; private final String CRLF = "\r\n"; public Response(OutputStream outputStream){ headInfo = new StringBuffer(); content = new StringBuilder(); bw = new BufferedWriter(new OutputStreamWriter(outputStream)); } private void createHeader(int code) { headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK); switch(code){ case 200 : headInfo.append("OK"); break; case 404 : headInfo.append("NOT FOUND"); break; case 500 : headInfo.append("SERVER ERROR"); break; } headInfo.append(CRLF); headInfo.append("Server:myServer Server/0.0.1").append(CRLF); headInfo.append("Date:").append(new Date()).append(CRLF); headInfo.append("Content-type:text/html;charset=UTF-8").append(CRLF); headInfo.append("Content-Length:").append(len).append(CRLF); headInfo.append(CRLF); //至关重要 头部和 内容要隔一行 } public Response print(String content){ this.content.append(content).append(CRLF); this.len += (content + CRLF).getBytes().length; return this; } public void pushToClient(int code) throws IOException { this.code = code; createHeader(code); bw.append(headInfo.toString()); bw.append(content.toString()); bw.flush(); bw.close(); } }5.业务抽象类Servlet.java
package com.httpServer.server; public abstract class Servlet { public void service(Request request, Response response){ doGet(request, response); doPost(request, response); } public abstract void doGet(Request request, Response response); public abstract void doPost(Request request, Response response); }6.ServletContext.java
package com.httpServer.server; import java.util.HashMap; import java.util.Map; /** * 存放 url -> servletName; servletName->servletClassPath 映射 * url:servletName 多对一 */ public class ServletContext { Map<String, String> servlet; //key: servletName, value:servlet 类路径 Map<String, String> mapping; //key: 请求路径 value:servletName public ServletContext(){ servlet = new HashMap<String, String>(); mapping = new HashMap<String, String>(); } public Map<String, String> getServlet() { return servlet; } public void setServlet(Map<String, String> servlet) { this.servlet = servlet; } public Map<String, String> getMapping() { return mapping; } public void setMapping(Map<String, String> mapping) { this.mapping = mapping; } }7.WebApp.java
package com.httpServer.server; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.util.List; import java.util.Map; public class WebApp { private static ServletContext servletContext; static{ servletContext = new ServletContext(); SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); try { SAXParser saxParser = saxParserFactory.newSAXParser(); WebHandler webHandler = new WebHandler(); saxParser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("WEB-INF/web.xml"), webHandler); Map<String, String> servlet = servletContext.getServlet(); List<Entity> entityList = webHandler.getEntityList(); for(Entity entity : entityList){ servlet.put(entity.getServletName(), entity.getServletClassPath()); } Map<String, String> mapping = servletContext.getMapping(); List<Mapping> mappingList = webHandler.getMappingList(); for(Mapping map : mappingList){ List<String> urlPatterns = map.getUrlPatterns(); for(String urlPattern : urlPatterns){ mapping.put(urlPattern, map.getServletName()); } } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 根据请求路径 获取对应的servletName * 然后根据servletName 获取 servlet的类路径(包名.类名) * 根据反射 返回servlet 实例 * * @param url * @return * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public static Servlet getServlet(String url) throws ClassNotFoundException, IllegalAccessException, InstantiationException { String servletClassPath = servletContext.getServlet().get(servletContext.getMapping().get(url)); return (Servlet) Class.forName(servletClassPath).newInstance(); } }8.xml文件解析的处理器类,WebHandler.java
package com.httpServer.server; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class WebHandler extends DefaultHandler { private List<Entity> entityList; private List<Mapping> mappingList; private Entity entity; private Mapping mapping; private String tagName; private boolean isServletMapping = false; @Override public void startDocument() throws SAXException { entityList = new ArrayList<Entity>(); mappingList = new ArrayList<Mapping>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println(qName); if(qName != null && !qName.trim().equals("")){ tagName = qName; if("servlet".equals(qName)){ entity = new Entity(); isServletMapping = false; }else if("servlet-mapping".equals(qName)){ mapping = new Mapping(); isServletMapping = true; } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { String tagContent = new String(ch, start, length); System.out.println(tagContent); if(tagName != null){ if(!isServletMapping){ if("servlet-name".equals(tagName)){ entity.setServletName(tagContent); }else if("servlet-class".equals(tagName)){ entity.setServletClassPath(tagContent); } }else{ if("servlet-name".equals(tagName)){ Iterator<Mapping> iterator = mappingList.iterator(); while(iterator.hasNext()){ Mapping map = iterator.next(); if(map.getServletName().equals(tagContent)){ mapping = map; iterator.remove(); } } mapping.setServletName(tagContent); }else if("url-pattern".equals(tagName)){ mapping.getUrlPatterns().add(tagContent); } } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if(qName != null && qName.trim().length() != 0){ if(!isServletMapping && "servlet".equals(qName)){ entityList.add(entity); }else if(isServletMapping && "servlet-mapping".equals(qName)){ mappingList.add(mapping); } tagName = null; } } @Override public void endDocument() throws SAXException { super.endDocument(); } public List<Entity> getEntityList() { return entityList; } public void setEntityList(List<Entity> entityList) { this.entityList = entityList; } public List<Mapping> getMappingList() { return mappingList; } public void setMappingList(List<Mapping> mappingList) { this.mappingList = mappingList; } public Entity getEntity() { return entity; } public void setEntity(Entity entity) { this.entity = entity; } public Mapping getMapping() { return mapping; } public void setMapping(Mapping mapping) { this.mapping = mapping; } public String getTagName() { return tagName; } public void setTagName(String tagName) { this.tagName = tagName; } public boolean isServletMapping() { return isServletMapping; } public void setServletMapping(boolean servletMapping) { isServletMapping = servletMapping; } }9.存储servletName与servlet类路径的映射类 Entity.java
package com.httpServer.server; /** * 存放 servletName -> servletNameClassPath的映射 */ public class Entity { private String servletName; private String servletClassPath; public String getServletName() { return servletName; } public void setServletName(String servletName) { this.servletName = servletName; } public String getServletClassPath() { return servletClassPath; } public void setServletClassPath(String servletClassPath) { this.servletClassPath = servletClassPath; } }10.,存储url与servletName的映射类,Mapping.java
package com.httpServer.server; import java.util.ArrayList; import java.util.List; /** * 存放url -> servletName的映射 */ public class Mapping { private List<String> urlPatterns = new ArrayList<String>(); private String servletName; public List<String> getUrlPatterns() { return urlPatterns; } public void setUrlPatterns(List<String> urlPatterns) { this.urlPatterns = urlPatterns; } public String getServletName() { return servletName; } public void setServletName(String servletName) { this.servletName = servletName; } }11.配置文件 web.xml
<?xml version="1.0" encoding="utf-8" ?> <web-app> <servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>com.httpServer.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/tempLogin</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>三、业务类 1.登录的业务类 LoginServlet
package com.httpServer.servlet; import com.httpServer.server.Request; import com.httpServer.server.Response; import com.httpServer.server.Servlet; public class LoginServlet extends Servlet { @Override public void doGet(Request request, Response response) { } @Override public void doPost(Request request, Response response) { String name = request.getParameter("uname"); String pass = request.getParameter("pass"); if("欣淡定".equals(name) && "123".equals(pass)){ response.print("<html><head><title>返回信息</title></head><body>欢迎 "+name +" 登录成功</body></html>"); }else{ response.print("用户名或密码错误"); } } }四,发送请求的页面html代码:
<html> <head> <title>登录</title> </head> <body> <form method="post" action="http://localhost:8080/tempLogin"> 昵称<input type="text" id="name" name="uname"><br/> 密码<input type="text" id="password" name="pass"/><br/> 体育运动:<input type="checkbox" name="sport" value="basketball"> 篮球 <input type="checkbox" name="sport" value="bennis"> 网球 <input type="checkbox" name="sport" value="football"> 足球</br> <input type="submit" value="提交"/> <input type="reset" value="重置"/> </form> </body> </html>五,登录测试: 六,测试结果
七、总结 测试的途中遇到的小问题: 1.发送post请求的时候,服务器会受到两个请求 2.页面发送请求的时候,服务端会收到一个url为 /favicon.icon的请求,导致控制台会有个空指针的异常;这个应该与页面标题的icon图标有关
以上就是模拟httpServer的一个完整demo
