自己动手写一个服务器,不能说水平会有多大的提升,但是让我知道servlet是如何与服务器进行交互的。现在将简单的模拟实现记录下来。(此处记录的是服务器模拟的简易版本,以后还会写一个利用注解实现的服务器版本)
分析图:
Request代码:
public class Request {
private static final String BLANK=" ";
private static final String CRLF="\r\n";
private String reqInfo;
private String method;
private String url;
private Map<String,List<String>> paremters=new HashMap<>();
public Request(Socket client) throws IOException{
this(client.getInputStream());
}
public Request(InputStream is) throws IOException{
byte[] buf=new byte[1024*1024];
int len=is.read(buf);
this.reqInfo=new String(buf,0,len);
//解析传入的数据信息
parseInfo();
}
private void parseInfo() {
//获取第一行数据
String fristLine=reqInfo.substring(0, reqInfo.indexOf(CRLF)).trim();
//获取请求方式
this.method=fristLine.substring(0,fristLine.indexOf(BLANK)).trim();
//声明变量存放请求参数
String paremter="";
if("post".equalsIgnoreCase(method)){
//post
this.url=fristLine.substring(fristLine.indexOf("/"),fristLine.indexOf("HTTP/1.1")).trim();
paremter=reqInfo.substring(reqInfo.lastIndexOf(CRLF)).trim();
}else if("get".equalsIgnoreCase(method)){
if(fristLine.contains("favicon.ico")){
return;//此种情况不做处理
}
this.url=fristLine.substring(fristLine.indexOf("/"),fristLine.indexOf("?"));
paremter=fristLine.substring(fristLine.indexOf("?")+1,fristLine.indexOf("HTTp/1.1")).trim();
}
//判定参数
if(paremter==null){
return;
}
//将参数放入map集合中
parseParemter(paremter);
}
/**
* 解析参数,将参数放入集合中
* @param paremter
*/
private void parseParemter(String paremter) {
String[] elements=paremter.split("&");
for(String element:elements){
String[] kvs=element.split("=");
String key=kvs[0];
List<String> values=null;
if(paremters.containsKey(key)){
values=paremters.get(key);
values.add(kvs[1]);
}else{
values=new ArrayList<>();
values.add(kvs[1]);
paremters.put(key, values);
}
}
}
public String getUrl(){
return url;
}
public String getMethod(){
return method;
}
public List<String> getParamters(String name){
if(paremters.containsKey(name)){
return paremters.get(name);
}
return null;
}
public String getParamter(String name){
if(paremters.containsKey(name)){
return paremters.get(name).get(0);
}
return null;
}
}
Response代码:
public class Response {
private static final String BLANK="";
private static final String CRLF="\r\n";
private static OutputStream os=null;
private StringBuffer respHandler;//响应头
private StringBuffer respContext;//响应实体
private int len;//响应实体内容的字节数
public Response(Socket client) throws IOException{
this(client.getOutputStream());
}
public Response(OutputStream os){
this.os=os;
respHandler=new StringBuffer();
respContext=new StringBuffer();
}
/*
* 拼装响应头
*/
private void createHandler(int code){
respHandler.append("HTTP1.1").append(BLANK).append(String.valueOf(code)).append(BLANK);
switch (code) {
case 200:
respHandler.append("OK");
break;
case 404:
respHandler.append("NOT FOUND");
case 500:
respHandler.append("SERVER ERROR");
}
respHandler.append(CRLF);
respHandler.append("Content-Type:text/html; charset=GBK").append(CRLF);
respHandler.append("Date:").append(new Date()).append(CRLF);
respHandler.append("Server:LAOXUEServer").append(CRLF);
respHandler.append("Content-Length:").append(this.len).append(CRLF);
respHandler.append(CRLF);
}
/*
* 要显示到浏览器上的内容
*/
public Response print(String respContext){
len+=respContext.getBytes().length;
this.respContext.append(respContext);
return this;
}
/**
* 通过流将封装好的响应内容写出到浏览器
* @param code
* @throws IOException
*/
public void pushToClient(int code) throws IOException{
createHandler(code);
this.respHandler.append(respContext);
os.write(respHandler.toString().getBytes());
}
}
Servlet代码:
public abstract class Servlet {
public void Service(Request req,Response resp) {
this.doGet(req, resp);
this.doPost(req, resp);
}
public abstract void doGet(Request req,Response resp);
public abstract void doPost(Request req,Response resp);
}
WebApp代码:
public class WebApp {
private Map<String,String> mapping;
private Map<String,String> servlet;
public WebApp() {
mapping =new HashMap<>();
servlet=new HashMap<>();
}
public Map<String, String> getMapping() {
return mapping;
}
public void setMapping(Map<String, String> mapping) {
this.mapping = mapping;
}
public Map<String, String> getServlet() {
return servlet;
}
public void setServlet(Map<String, String> servlet) {
this.servlet = servlet;
}
}
ServletContext代码:
public class ServletContext {
//测试数据
private static WebApp app=new WebApp();
static{
app.getMapping().put("/login", "abc");
app.getMapping().put("/log","abc");
app.getServlet().put("abc","tomcat.controller.LoginServlet");
}
public Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
//使用反射技术创建对应的对象
return (Servlet) Class.forName(app.getServlet().get(app.getMapping().get(url))).newInstance();
}
}
Server代码:
public class Server {
public static void main(String[] args) throws IOException {
//创建server对象
ServerSocket server=new ServerSocket(8888);
while(true){
//获取客户端Socket
Socket client=server.accept();
new Thread(new Dispatcher(client)).start();
}
}
}
Dispacher代码:
public class Dispatcher implements Runnable {
private Request req;
private Response resp;
private ServletContext context;
public Dispatcher(Socket client) {
try {
req = new Request(client);
resp = new Response(client);
context = new ServletContext();
} catch (Exception e) {
}
}
@Override
public void run() {
String url=req.getUrl();
if(url!=null && !("".equals(url))){
try {
context.getServlet(req.getUrl()).Service(req, resp);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
LoginServlet代码:
public class LoginServlet extends Servlet{
@Override
public void doGet(Request req, Response resp) {
String name=req.getParamter("uname");
resp.print("<!DOCTYPE html><html><head></head><body><h1>欢迎"+name+"登陆</h1></body></html>");
try {
resp.pushToClient(200);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(Request req, Response resp) {
}
}
代码的组织结构:
前端测试:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>登陆</title>
</head>
<body>
<form action="http://localhost:8888/login" method="post">
用户名:<input type="text" name="uname" />
密码:<input type="password" name="pwd"/>
<input type="submit" value="登陆"/>
</form>
</body>
</html>