手写服务器总结
搭建框架
…………………
实现
编写web.xml
1 2 3 4 5 6 7 8 9 10 11 12
| <servlet> <servlet-name>login</servlet-name> <servlet-class>com.szxy.httpserver.servlet.LoginServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> <url-pattern>/log</url-pattern> </servlet-mapping>
|
实体类保存解析xml中的信息
Entity类:servlet-name,servlet-class
Mapping类:servlet-name,url-pattern
解析xml
dom4j解析xml
Entity:servlet-name,servlet-class
Entity通过String name,String clazz两个成员变量保存web.xml中的\servlet-name,servlet-class
Mapping:servlet-name,url-pattern,
Mapping通过String name,和List urlPattern集合两个成员变量保存web.xml中的\servlet-name,url-pattern,
ServletContext类
上下文,是一个容器,描述Entity与Mapping之间的关系
该类的成员变量
1 2
| private Map<String, String> servlet; //key是servlet-name,value是servlet-class private Map<String, String> mapping; //key是url-pattern,value是servlet-name
|
WebApp类
1)将Entity和Mapping中的servlet-name,servlet-class,url-pattern,servlet-name
保存到ServletContext类的成员变量中。
请求路径作为key,获取servlet-name值,获取servlet-name
servlet-name作为key,获取servlet的 全路径名(包名+类名)
1) 反射创建Servlet
可以通过全路径名反射创建Servlet对象
1 2 3 4 5 6 7
| String servletName = context.getMapping().get(url);
String servletClass = context.getServlet().get(servletName);
Class<?> clazz = Class.forName(servletClass); servlet = (Servlet) clazz.newInstance();
|
servlet
建立抽象类Servlet
1 2 3 4 5 6 7 8
| public abstract class Servlet { public void service(Request req, Response rep)throws Exception{ this.doGet(req, rep); this.doPost(req, rep); } public abstract void doGet(Request req, Response rep) throws Exception; public abstract void doPost(Request req, Response rep) throws Exception; }
|
处理请求的Servlet继承Servlet
例:登录LoginServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class LoginServlet extends Servlet{ @Override public void doGet(Request req, Response rep) throws Exception { String name = req.getParater("username"); String pwd = req.getParater("pwd"); if("bjsxt".equals(name) && "123".equals(pwd)){ rep.println(name + "登录成功"); }else{ rep.println("账号或者密码不正确"); } }
@Override public void doPost(Request req, Response rep) throws Exception { } }
|
封装Request对象(浏览器的请求)
通过输入流获取浏览器的请求信息,然后根据HTTP协议,进行请求信息的处理
从请求信息中获取请求方式,路径,参数,
例:GET请求和POST请求的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| GET /log?username=bj&pwd=123&hobby=ball HTTP/1.1 Host: localhost:8888 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9
POST /log HTTP/1.1 Host: localhost:8888 Connection: keep-alive Content-Length: 30 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: null Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9
username=bj&pwd=123&hobby=ball
|
请求参数的乱码问题
处理中文,因为浏览器对中文进行了编码,因此需要解码
服务器端获取到发过来的请求参数默认使用ISO8859-1进行解码操作,中文一定有乱码问题
1 2
| //value是请求参数,code是编码 return URLDecoder.decode(value, code);//decode(keyValues[1].trim(), "utf-8")
|
封装Response对象(服务器的响应)
1.严格根据HTTP协议封装响应信息
1 2 3 4 5 6 7 8 9
| //响应头 //协议,状态码,描述 headInfo.append("HTTP1.1").append(BLANK).append(code).append(BLANK).append(""); headInfo.append(CRLF); headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF); headInfo.append("Content-Length:"+length).append(CRLF); headInfo.append(CRLF); //响应正文 content.append(“<!DOCTYPE html><html><head><title>登录响应</title></head><body>登录成功</body></html>”).append(CRLF);
|
2.响应正文可以调用Response类中的println(String info)进行设置
content.append(info).append(CRLF);
封装分发器Dispatcher
Dispatcher是一个请求与响应,并实现了Runnable接口
通过客户端连接的对象client,初始化Request和Response对象
1 2
| req = new Request(client.getInputStream()); rep = new Response(client.getOutputStream());
|
run()方法实现
1.根据请求信息获取路径,
2.根据路径通过反射创建servlet
3.调用servlet的service()方法处理请求
4.rep.pushToClient(code);//响应,将封装后的Response信息发送给客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public void run() { Servlet servlet = WebApp.getServlet(req.getUrl()); if(servlet == null){ this.code = 404; }else{ try { servlet.service(req, rep); } catch (Exception e) { e.printStackTrace(); this.code = 500; } } rep.pushToClient(code); IOCloseUtil.closeAll(client); }
|
ServerSocket服务器
创建一个ServelSocket对象,监听8888端口
1
| server = new ServerSocket(port);
|
循环接收客户端请求,接收到请求,创建一个线程代理Dispatcher对象处理请求
1 2 3 4 5 6 7 8
| while(!isShutDown){ Socket client = server.accept(); Dispatcher dis = new Dispatcher(client); new Thread(dis).start(); }
|
解决浏览器请求的icon
该请求是浏览器默认发送的:http://localhost:8888/favicon.ico
解决:
1.在web.xml中配置路径
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>favicon</servlet-name> <servlet-class>com.szxy.httpserver.servlet.FaviconServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>favicon</servlet-name> <url-pattern>/favicon.ico</url-pattern> </servlet-mapping>
|
2.创建FaviconServlet类继承Servlet