通过一个xml文件来确定服务器要如何响应相关请求
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com/xys/server/basic/LoginServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>reg</servlet-name>
<servlet-class>com/xys/server/basic/RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
<url-pattern>/g</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>reg</servlet-name>
<url-pattern>/reg</url-pattern>
</servlet-mapping>
</web-app>
在这个例子中如果浏览器做了/login或/g请求,则服务器就需要执行com.xys.server.basic.LoginServlet类中的操作
下面四个类用于解析web.xml以判断何时需要执行哪个类中的操作,再创建相应的响应类做出响应
package com.xys.server.basic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//将entities容器和mappings容器转换为Map容器
public class WebContext {
private final Map<String, String> EntityMap = new HashMap<>();
private final Map<String, String> MappingMap = new HashMap<>();
public WebContext(List<Entity> entities, List<Mapping> mappings){
for(Entity en :entities){
EntityMap.put(en.getName(), en.getClz());
}
for(Mapping ma :mappings){
for(String pa:ma.getPatterns() ){
MappingMap.put(pa, ma.getName());
}
}
}
//通过url获取相应类的地址
public String getClz(String pattern){
return EntityMap.get(MappingMap.get(pattern));
}
}
package com.xys.server.basic;
public class Entity {
private String name;
private String clz;
//保存web.xml中一个servlet标签中的相关内容
public Entity(){
}
public void setName(String name) {
this.name = name;
}
public void setClz(String clz) {
this.clz = clz;
}
public String getName() {
return name;
}
public String getClz() {
return clz;
}
}
package com.xys.server.basic;
import java.util.HashSet;
import java.util.Set;
//保存web.xml中一个servlet-mapping标签中的相关内容
public class Mapping {
private String name;
private Set<String> patterns;
public Mapping(){
patterns = new HashSet<String>();
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setPatterns(Set<String> patterns) {
this.patterns = patterns;
}
public Set<String> getPatterns() {
return patterns;
}
public void addPatterns(String pattern){
patterns.add(pattern);
}
}
package com.xys.server.basic;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
//解析WebXML
public class SaxAnalyze {
public void Analyze(String pattern, String parameter, Socket client) throws Exception {
Response response = new Response(client);
//1、获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//2、从解析工厂获取解析器
SAXParser parser = factory.newSAXParser();
//4、加载处理器
WebHandle handle = new WebHandle();
//5、解析:执行处理器中的方法,在该例子中,处理器会根据Web.xml中的内容创建servlet对象和servlet-mapping对象,并将其存入容器
parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/xys/server/basic/Web.xml"), handle);
//调用WebContext类将解析获取的servlet对象和servlet-mapping对象存入map,方便通过servlet-mapping找到对应的servlet,再通过servlet找到对应的类
WebContext wc = new WebContext(handle.getEntities(),handle.getMappings());
//通过url获取相应类的地址
String index = "<html>" +
"<head>" +
"<title>" +
"index" +
"</title>" +
"</head>" +
"<body>" +
"MyServer的回复:主页" +
"</body>" +
"</html>";
//url请求为空则进入主页
if(pattern == null || pattern.equals("") || pattern.equals("/"))
response.PushToBrowser(200, index);
else {
//url请求不为空,则根据web.xml的内容创建相关类
String name = wc.getClz(pattern);
if (name != null) {
name = name.replace("/", ".");
//利用反射创建相关类
Class<?> Clz = Class.forName(name);
//声明一个响应接口,用该接口接收一个响应实现类对象,根据不同的请求会实例化不同的响应实现类对象
Servlet servlet = (Servlet) Clz.getConstructor().newInstance();
servlet.Service(parameter, client);
//请求不存在返回404页
} else {
String errorContent = "<html>" +
"<head>" +
"<title>" +
"error" +
"</title>" +
"</head>" +
"<body>" +
"MyServer的回复:404 NOT FOUND" +
"</body>" +
"</html>";
response.PushToBrowser(404, errorContent);
}
}
}
//3、编写处理器
private static class WebHandle extends DefaultHandler{
private final List<Entity> entities = new ArrayList<>();
private final List<Mapping> mappings = new ArrayList<>();
private Entity entity;
private Mapping mapping;
String tag;
boolean isMapping = false;
//建servlet对象和servlet-mapping对象
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
tag = qName;
if(tag.equals("servlet")){
entity = new Entity();
}else if(tag.equals("servlet-mapping")){
mapping = new Mapping();
isMapping = true;
}
}
//根据XML中的内容为对象的属性赋值
@Override
public void characters(char[] ch, int start, int length) {
String str = new String(ch, start, length);
if(tag != null && !str.equals("")){
if(isMapping){
if(tag.equals("servlet-name")){
mapping.setName(str);
}else if(tag.equals("url-pattern")){
mapping.addPatterns(str);
}
}else{
if(tag.equals("servlet-name")){
entity.setName(str);
}else if(tag.equals("servlet-class")){
entity.setClz(str);
}
}
}
}
//将对象放入容器
@Override
public void endElement(String uri, String localName, String qName) {
if (qName.equals("servlet")) {
entities.add(entity);
} else if (qName.equals("servlet-mapping")) {
mappings.add(mapping);
}
tag = null;
}
public List<Entity> getEntities() {
return entities;
}
public List<Mapping> getMappings() {
return mappings;
}
}
}
响应接口,所有响应类均实现该接口,这样在做出响应时只需要声明一个相应接口,然后调用接口中相应的方法即可,上面的方法会自行根据请求实例化不同的响应实现类
package com.xys.server.basic;
import java.net.Socket;
public interface Servlet {
void Service(String parameter, Socket client);
}
两个实现类:实现类可以接受请求参数,并执行将请求参数放入map中的处理操作
登录
package com.xys.server.basic;
import java.net.Socket;
import java.util.*;
//登录
public class LoginServlet implements Servlet{
@Override
public void Service(String parameter, Socket client) {
Map<String, List<String>> ServiceParameter = Convert.convertMap(parameter);
System.out.println("参数为:"+parameter);
for(String key:ServiceParameter.keySet()){
System.out.print(key+": ");
List<String> values = ServiceParameter.get(key);
String[] valueArray = values.toArray(new String[0]);
for(String value:valueArray){
System.out.print(value+" ");
}
System.out.println();
}
Response response = new Response(client);
//编写响应正文
String content = "<html>" +
"<head>" +
"<title>" +
"success" +
"</title>" +
"</head>" +
"<body>" +
"LoginServlet的回复:登录成功" +
"</body>" +
"</html>";
response.PushToBrowser(200, content);
}
}
注册
package com.xys.server.basic;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
import java.util.Map;
//注册
public class RegisterServlet implements Servlet{
@Override
public void Service(String parameter, Socket client) {
Map<String, List<String>> RegisterParameter = Convert.convertMap(parameter);
System.out.println("参数为:"+parameter);
for(String key:RegisterParameter.keySet()){
System.out.print(key+": ");
List<String> values = RegisterParameter.get(key);
String[] valueArray = values.toArray(new String[0]);
for(String value:valueArray){
System.out.print(value+" ");
}
System.out.println();
}
try{
Response response = new Response(client.getOutputStream());
//编写响应正文
String content = "<html>" +
"<head>" +
"<title>" +
"success" +
"</title>" +
"</head>" +
"<body>" +
"RegisterServlet的回复:用户注册" +
"</body>" +
"</html>";
response.PushToBrowser(200, content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.xys.server.basic;
import java.util.*;
public class Convert {
//将请求参数转为map容器
public static Map<String, List<String>> convertMap(String parameter) {
Map<String, List<String>> parameterM = new HashMap<>();
String[] KeyValues = parameter.split("&");
for (String query : KeyValues) {
String[] kv = query.split("=");
kv = Arrays.copyOf(kv, 2);//避免有些参数是空值导致kv的长度只有1
String key = kv[0];
String value = kv[1];
//如果这个key还没有被存进map,就在容器中新建一个键值对
if (!parameterM.containsKey(key))
parameterM.put(key, new ArrayList<String>());
//获取相应的key值所对应的value容器,将value存入
parameterM.get(key).add(value);
}
return parameterM;
}
}
向浏览器发送响应的类,任何类都可以调用该类向浏览器发送响应,例如上面的两个响应实现类就有调用这个类
package com.xys.server.basic;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;
//服务器响应请求
public class Response {
private BufferedWriter bw;
private String content;
private StringBuilder headInfo;
private int len;
private final String BLANK = " ";
private final String CRLF = "\r\n";
private Response(){
content = "";
headInfo = new StringBuilder();
len = 0;
}
public Response(Socket client){
this();
try{
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public Response(OutputStream os){
this();
try{
bw = new BufferedWriter(new OutputStreamWriter(os));
} catch (Exception e) {
e.printStackTrace();
}
}
//建立响应协议并发送
public void PushToBrowser(int code, String content) {
if(headInfo == null)
code = 505;
len = content.getBytes(StandardCharsets.UTF_8).length;
buildHead(code);
try{
bw.append(headInfo);
bw.append(content);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//建立状态行和响应头
private void buildHead(int code){
headInfo.append("HTTP/1.1").append(BLANK);
headInfo.append(code).append(BLANK);
switch(code){
case 200:
headInfo.append("OK").append(CRLF);
break;
case 404:
headInfo.append("NOT FOUND").append(CRLF);
break;
case 505:
headInfo.append("SERVER ERROR").append(CRLF);
break;
}
headInfo.append("Date:").append(new Date()).append(CRLF);
headInfo.append("Server:MyServer/0.01;charset:UTF-8").append(CRLF);
headInfo.append("Content-type:text/html").append(CRLF);
headInfo.append("Content-length:").append(len).append(CRLF).append(CRLF);
}
}
主类,同时用于获取请求参数,并将参数传给响应实现类进行进一步操作
package com.xys.server.basic;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//主类
public class Main {
private static boolean isRunner = true;
public static void main(String[] args) throws Exception {
ServerSocket s_socket = new ServerSocket(8888);
while(isRunner){
Socket client = s_socket.accept();
System.out.println("------------------------------");
System.out.println("一个客户端建立了连接");
new Thread(new Dispatcher(client)).start();
}
}
//封装内部类:多线程获取不同的请求内容
private static class Dispatcher implements Runnable{
private final Socket client;
public Dispatcher(Socket client){
this.client = client;
}
@Override
public void run() {
try{
SaxAnalyze sa = new SaxAnalyze();
//获取请求协议
InputStream is = client.getInputStream();
byte[] flush = new byte[1024 * 10];
int len = is.read(flush);
String info = new String(flush, 0, len);
//从请求协议中分离URI
int idx1 = info.indexOf("/");
int idx2 = info.indexOf(" ", idx1);
String pattern = info.substring(idx1, idx2);
//从请求协议中分离参数
String parameter = info.substring(info.lastIndexOf("\r\n") + 1);
System.out.println("请求协议:" + "\r\n" + info);
System.out.println("请求的服务为:" + pattern);
//解析xml,并根据获得的URI创建对应的对象,并将获得的参数传给创建的对象
sa.Analyze(pattern, parameter, client);
client.close();
} catch (Exception e) {
e.printStackTrace();
isRunner = false;
}
}
}
}