手写服务器-1~一次往返
2020-02-29 本文已影响0人
风中小酌
- 搭建项目框架
项目结构:
- XML配置文件:用于存储servlet与http访问路径的对应关系
- server 服务器相关的类
Request 请求
Response 响应
Server 服务器的启动和停止
WebApp 初始化程序运行的数据,根据不同的url通过反射机制创建servlet对象
WebDom4J 解析XML文件 - servlet类:用于保存接口类
- 工具类
创建XML配置文件
(建立名称与servlet类名,名称与http的url路径之间的映射关系)
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.test.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<servlet-pattern>/login</servlet-pattern>
<servlet-pattern>/log</servlet-pattern>
</servlet-mapping>
</web-app>
创建Request请求对象
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//请求
public class Request {
private String requestInfo; //请求信息
private String method; //请求方法
private String url; //URL
//存储表单数据
Map<String, List<String>> formvalues;
//换行
private final static String CRLF = "\r\n";
//构造方法
public Request(){
formvalues = new HashMap<String, List<String>>();
method = "";
url = "";
requestInfo = "";
}
//带参构造
public Request(InputStream is ){
this();
byte[] buf = new byte[20480];
int len = 0;
try {
len = is.read(buf);
this.requestInfo = new String(buf, 0, len);
} catch (IOException e) {
e.printStackTrace();
}
//解析请求
this.parseRequest(requestInfo);
}
public void parseRequest(String reqinfo){
//存储请求参数
String reqParameters = "";
//解析收到请求的第一行
String firstLine = reqinfo.substring(0, reqinfo.indexOf(CRLF));
int index = firstLine.indexOf("/");
//分析出请求方式
method = firstLine.substring(0, index).trim();
//分解URL,可能包含参数,也可能不含参数
String urlString = firstLine.substring(index, firstLine.indexOf("HTTP/")).trim();
if("get".equalsIgnoreCase(method)){
//如果URL中包含?,后边即为请求参数
if(urlString.contains("?")){
//需要对问号进行转义
String[] urlarr = urlString.split("\\?");
this.url = urlarr[0];
reqParameters = urlarr[1];
}else{
this.url = urlString;
}
}else{//请求为POST
this.url = urlString;
reqParameters = reqinfo.substring(reqinfo.lastIndexOf(CRLF)).trim();
}
if(reqParameters.equals("")){
return;
}
//解析请求中的各个参数
this.parseParam(reqParameters);
}
public void parseParam(String reqparam){
//分离不同的参数
String[] token = reqparam.split("&");
//分离参数及其对应的数值
for(String item : token){
//获取键和值
String[] arritem = item.split("=");
//判断键对应的数据是否存在
if(arritem.length == 1){
arritem = Arrays.copyOf(arritem, 2);
//不存在时,将值设置为NULL
arritem[1] = null;
}
String key = arritem[0];
String value = arritem[1] == null ? null : decode(arritem[1], "UTF-8");
//判断键在MAP中是否存在
if(!formvalues.containsKey(key)){
//不存在时,对数值的容器进行初始化
formvalues.put(key, new ArrayList<String>());
}
List<String> values = formvalues.get(key);
values.add(value);
formvalues.put(key, values);
}
}
public String getRequestInfo() {
return requestInfo;
}
public void setRequestInfo(String requestInfo) {
this.requestInfo = requestInfo;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, List<String>> getFormvalues() {
return formvalues;
}
public void setFormvalues(Map<String, List<String>> formvalues) {
this.formvalues = formvalues;
}
/**
* 根据键获取表单中对应的参数值列表
*/
public String[] getParameters(String name){
List<String> values = formvalues.get(name);
if(values == null){
return null;
}else{
return values.toArray(new String[0]);
}
}
//根据键获取参数名,获取参数
public String getParameter(String name){
String[] values = this.getParameters(name);
if(values == null){
return null;
}else{
return values[0];
}
}
/**
* 对请求中的数据进行解码
* @param value
* @param code
* @return
*/
public String decode(String value, String code){
try {
return URLDecoder.decode(value, code);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
创建Reponse响应对象
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import com.test.util.IOCloseUtil;
//响应
public class Response {
//响应头
private StringBuffer header;
//响应内容
private StringBuffer content;
//响应内容的长度
private int length = 0;
//流
private BufferedWriter bw;
//字符串常量
private static String CRLF = "\r\n";
private static String BLANK = " ";
public Response(){
this.header = new StringBuffer();
this.content = new StringBuffer();
}
public Response(OutputStream os) {
this();
try {
bw = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
} catch (UnsupportedEncodingException e) {
header = null;
}
}
//构造单行文本
public Response print(String info){
content.append(info);
try {
length += info.getBytes("utf-8").length;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return this;
}
//构造多行文本
public Response println(String info){
content.append(info).append(CRLF);
try {
length += (info + CRLF).getBytes("UTF-8").length;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return this;
}
//构造响应头
public void createHeaderInfo(int code){
header.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
switch(code){
case 200:
header.append("OK");
break;
case 500:
header.append("SERVER ERROR");
break;
default:
header.append("NOT FOUND");
}
header.append(CRLF );
header.append("Content-Type:text/html;charset=utf-8 ").append(CRLF);
header.append("Content-Length:" + length).append(CRLF);
header.append(CRLF);
}
//推送数据到客户端
public void push2Client(int code){
if(header == null){
code = 500;
}
//调用构造响应头
this.createHeaderInfo(code);
try {
bw.write(header.toString());
bw.write(content.toString());
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
close();
}
public void close(){
IOCloseUtil.closeAll(bw);
}
}
创建Server启动停止服务器
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import com.test.util.IOCloseUtil;
/**
* 服务的启动与停止
* @author Administrator
*
*/
public class Server {
private ServerSocket server;
private boolean isShutdown = false;
public static void main(String[] args) {
Server ss = new Server();
ss.start(8888);
}
//开启服务
public void start(int port){
try {
this.server = new ServerSocket(port);
this.receive();
} catch (IOException e) {
isShutdown = true;
}
}
//接收请求
public void receive(){
while(!isShutdown){
try {
Socket client = server.accept();
} catch (IOException e) {
shutdown();
}
}
}
public void shutdown(){
isShutdown = true;
IOCloseUtil.closeAll(server);
}
}
创建servlet名称与servlet类映射关系类
//存储servlet-name 与 servlet-class 映射关系
public class Entity {
private String name;
private String clazz;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public Entity(String name, String clazz) {
this.name = name;
this.clazz = clazz;
}
@Override
public String toString() {
return "Entity [name=" + name + ", clazz=" + clazz + "]";
}
public Entity() {
}
}
创建servlet名称与HTTP请求的URL之间映射关系类
import java.util.ArrayList;
import java.util.List;
//存储servlet-name servlet-mapping映射关系
public class Mapping {
private String name;
private List<String> pattern;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getPattern() {
return pattern;
}
public void setPattern(List<String> pattern) {
this.pattern = pattern;
}
public Mapping(String name, List<String> pattern) {
super();
this.name = name;
this.pattern = pattern;
}
public Mapping() {
pattern = new ArrayList<String>();
}
@Override
public String toString() {
return "Mapping [name=" + name + ", pattern=" + pattern + "]";
}
}
用于存储XML中servlet与servlet-mapping之间映射关系
import java.util.HashMap;
import java.util.Map;
/**
* 用于存储servlet 与 mapping 的映射关系
* @author Administrator
*
*/
public class ServletContext {
private Map<String, String> servlet; //servlet-name, servlet-class, 一对一
private Map<String, String> mapping; //servlet-pattern, servlet-name, 多对一
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;
}
public ServletContext(Map<String, String> servlet,
Map<String, String> mapping) {
super();
this.servlet = servlet;
this.mapping = mapping;
}
public ServletContext() {
servlet = new HashMap<String, String>();
mapping = new HashMap<String, String>();
}
}
解析XML文件的类
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
//读取服务器XML配置
public class WebDom4J {
private List<Entity> entity;
private List<Mapping> mapping;
public List<Entity> getEntity() {
return entity;
}
public void setEntity(List<Entity> entity) {
this.entity = entity;
}
public List<Mapping> getMapping() {
return mapping;
}
public void setMapping(List<Mapping> mapping) {
this.mapping = mapping;
}
public WebDom4J(List<Entity> entity, List<Mapping> mapping) {
super();
this.entity = entity;
this.mapping = mapping;
}
public WebDom4J() {
entity = new ArrayList<Entity>();
mapping = new ArrayList<Mapping>();
}
//获取Document对象
public Document getDocument(){
//创建SAVReader对象
SAXReader sr = new SAXReader();
Document doc = null;
try {
doc = sr.read(new FileInputStream("src/WEB_INFO/web.xml"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
return doc;
}
//解析XML
public void parseXML(Document doc){
//获取XML文件顶级节点
Element root = doc.getRootElement();
for(Iterator<Element> eleite = root.elementIterator("servlet"); eleite.hasNext();){
Element ele = eleite.next();
Entity ent = new Entity();
for(Iterator<Element> subite = ele.elementIterator();subite.hasNext();){
Element subele = subite.next();
String name = subele.getName();
if(name.equals("servlet-name")){
ent.setName(subele.getText());
}else{
ent.setClazz(subele.getText());
}
}
entity.add(ent);
}
for(Iterator<Element> eleite = root.elementIterator("servlet-mapping");eleite.hasNext();){
Element ele = eleite.next();
Mapping map = new Mapping();
for(Iterator<Element> subite = ele.elementIterator();subite.hasNext();){
Element subele = subite.next();
String name = subele.getName();
if(name.equals("servlet-name")){
map.setName(subele.getText());
}else{
map.getPattern().add(subele.getText());
}
}
mapping.add(map);
}
}
}
创建WebApp类,用于初始化程序运行的数据
import java.util.List;
import java.util.Map;
import com.test.servlet.Servlet;
// 初始化程序运行的数据,根据不同的url创建所请求的servlet对象
public class WebApp {
private static ServletContext context;
static{
context = new ServletContext();
//分别获取对应关系的Map集合
Map<String, String> servletEntity = context.getServlet();
Map<String, String> mapping = context.getMapping();
//读取XML
WebDom4J web = new WebDom4J();
web.parseXML(web.getDocument());
//获取解析XML后得到的集合
List<Entity> entlist = web.getEntity();
List<Mapping> maplist = web.getMapping();
//将集合中的数据存储到Map
for(Entity ent : entlist){
servletEntity.put(ent.getName(), ent.getClazz());
}
for(Mapping m : maplist){
List<String> urlPattern = m.getPattern();
for(String url : urlPattern){
mapping.put(url, m.getName());
}
}
}
/**
* 根据URL创建不同的Servlet对象
*/
public static Servlet getServlet(String url){
//判断URL是否为空
if(url == null || url.trim().equals("")){
return null;
}
//根据URL获取对应的Servlet-name
String servletName = context.getMapping().get(url);
System.out.println(servletName);
//根据servlet-name获取对应的Servlet-class
String clazz = context.getServlet().get(servletName);
try {
//通过反射获取对象
Class<?> servCls = Class.forName(clazz);
//根据无参构造方法,创建Servlet对象
Servlet servlet = (Servlet) servCls.newInstance();
return servlet;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
System.out.println(WebApp.getServlet("/log"));
System.out.println(WebApp.getServlet("/login"));
}
}
创建Servlet父类
import com.test.server.Request;
import com.test.server.Response;
/**
* 所有请求Servlet的父类
* @author Administrator
*
*/
public abstract class Servlet {
public void Service(Request req, Response res) throws Exception{
doGet(req, res);
doPost(req, res);
}
public abstract void doGet(Request req, Response res) throws Exception;
public abstract void doPost(Request req, Response res) throws Exception;
}
创建用于处理登录请求的LoginServlet类
import com.test.server.Request;
import com.test.server.Response;
public class LoginServlet extends Servlet {
@Override
public void doGet(Request req, Response res) throws Exception {
String username = req.getParameter("username");
String password = req.getParameter("password");
if(login(username, password)){
res.println("登录成功, 欢迎你: " + username);
}else{
res.println("抱歉, 用户: " + username + "登录失败!");
}
}
@Override
public void doPost(Request req, Response res) throws Exception {
// TODO Auto-generated method stub
}
private boolean login(String username, String pwd){
if(username.equals("test") && pwd.equals("123456")){
return true;
}else{
return false;
}
}
}
工具类,用于关闭对象流
import java.io.Closeable;
import java.io.IOException;
public class IOCloseUtil {
/**
* 关闭对象流
* @param ables
*/
public static void closeAll(Closeable...ables){
for(Closeable c : ables){
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}