相信每一个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);
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;
}
}
}
}
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{
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;
public class ServletContext {
Map<String, String> servlet;
Map<String, String> mapping;
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();
}
}
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;
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;
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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)