手写服务器httpserver_封装分发器和多请求处理

来源:互联网 发布:柯维时间矩阵图 编辑:程序博客网 时间:2024/06/05 12:42

手写服务器httpserver_封装分发器

前面所述的分装请求和响应其实都只是对单个请求有效,如果需要对多个请求有效的话,就需要加入多线程。

import java.io.IOException;import java.net.ServerSocket;public class MyServer {    private ServerSocket server ;    private static final String CRLF = "\r\n";    private static final String blank = " ";    private boolean flag = true;    private int i = 0;    public static void main(String[] args) {        new MyServer().start();    }    public void start(){        start(10001);    }    public void start(int port){        try {            server = new ServerSocket(port);            receive();        } catch (IOException e) {//          e.printStackTrace();            stop();        }    }    public void receive(){        try {            //请求及响应            while(flag){                new Thread(new Dispacher(server.accept())).start();            }        } catch (IOException e) {//          e.printStackTrace();            stop();        }    }    public void stop(){        try {            flag = false;            server.close();        } catch (IOException e) {            e.printStackTrace();        }    }}
import java.io.IOException;public class Servlet {    public void service(Request req,Response re) throws IOException{        re.println("<html><head><title>HTML响应实例</title>");        re.println("</head><body>");        re.println("欢迎").println(req.getParameterValue("uname"));        re.println("</body></html>");    }}
import java.io.IOException;import java.net.Socket;/** * 加入多线程,一个请求与响应,就一个对象 */public class Dispacher implements Runnable{    private Request req;    private Response re;    private Socket soc;    private int code;    public Dispacher(Socket soc) {        this.soc = soc;        try {            req = new Request(soc.getInputStream());            re = new Response(soc);        } catch (IOException e) {//          e.printStackTrace();            code = 500;            return ;        }    }    @Override    public void run() {        Servlet s = new Servlet();        try {            s.service(req, re);            re.pushToClient(code);            soc.close();          } catch (IOException e) {//          e.printStackTrace();            try {                re.pushToClient(500);                soc.close();            } catch (IOException e1) {                e1.printStackTrace();            }        }    }}

手写服务器httpserver_多请求处理_多态

如果我们有不同的Servlet,这个时候就需要加入多态的概念,也就是根据不同的请求,用不用的Servlet服务,这里利用工厂模式:客户端发送请求至服务器,服务器启动并调用 Servlet,Servlet 根据客户端请求生成响应内容并将其传给服务器,服务器将响应返回客户端。

一、先封装接收和发送消息

package cn.feng.http_2;import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.StringTokenizer;public class Request2 {    //请求方式    private String method;    //请求资源    private String url;    //请求参数    private Map<String,List<String>> parameterMapValues;    //内部    public static final String CRLF = "\r\n";    private InputStream is;    private String requestInfo;    public Request2(){        method = "";        url = "";        parameterMapValues = new HashMap<String,List<String>>();        requestInfo = "";    }    public Request2(InputStream is){        this();        this.is = is;        try {            byte[] data = new byte[204800];            int len = is.read(data);            requestInfo = new String(data,0,len);        } catch (IOException e) {            return;        }        //分析请求信息        parseRequestInfo();    }    public String getUrl() {        return url;    }    public void setUrl(String url) {        this.url = url;    }    /**     * 分析请求信息     */    private void parseRequestInfo(){        if((null==requestInfo) || (requestInfo=requestInfo.trim()).equals("")){            return;        }        /**         * ====================================         * 从信息的首行分解出:请求方式  请求路径  请求参数(get可能存在)         *   如:GET /index.html?uname=intputUname&pwd=inputPassword HTTP/1.1         *          * 如果为post方式,请求参数可能在最后正文中         * ====================================         */        String paramString = "";//接收请求参数        //1、获取请求方式        String firstLine = requestInfo.substring(0,requestInfo.indexOf(CRLF));        int idx = requestInfo.indexOf("/");// /的位置        this.method = firstLine.substring(0,idx).trim();        String urlStr = firstLine.substring(idx,firstLine.indexOf("HTTP/")).trim();        if(this.method.equalsIgnoreCase("post")){//post方式            this.url = urlStr;            paramString = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();        }else if(this.method.equalsIgnoreCase("get")){//get方式            if(urlStr.contains("?")){                String[] urlArray = urlStr.split("\\?");                this.url = urlArray[0];                paramString = urlArray[1];//接收请求参数            }else{                this.url = urlStr;            }        }        //2、将请求参数封装到Map中        parseParams(paramString);    }    /**     * 将请求参数封装到Map中     * @param paramString     */    private void parseParams(String paramString){        //分割,将字符串转成数组        StringTokenizer token = new StringTokenizer(paramString,"&");        while(token.hasMoreTokens()){            String keyValue = token.nextToken();            String[] keyValues = keyValue.split("=");            if(keyValues.length == 1){                keyValues = Arrays.copyOf(keyValues, 2);                keyValues[1] = null;            }            String key = keyValues[0].trim();            String value = null==keyValues[1]?null:decode(keyValues[1].trim(),"gbk");            //分拣,转换成Map            if(!parameterMapValues.containsKey(key)){                parameterMapValues.put(key, new ArrayList<String>());            }            List<String> values = parameterMapValues.get(key);            values.add(value);        }    }    /**     * 解决中文     * @param value     * @param code     * @return     */    private String decode(String value,String code){        try {            return java.net.URLDecoder.decode(value, code);        } catch (UnsupportedEncodingException e) {            //e.printStackTrace();        }        return null;    }    /**     * 根据页面的name获取对应的多个值     */    public String[] getParameterValues(String name){        List<String> values = null;        if( (values=parameterMapValues.get(name))==null ){            return null;        }else{            return values.toArray(new String[0]);        }    }    /**     * 根据页面的name获取对应的单个值     */    public String getParameterValue(String name){        String[] values = getParameterValues(name);        if(null==values){            return null;        }        return values[0];    }}
package cn.feng.http_2;import java.io.BufferedWriter;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.net.Socket;import java.util.Date;/** * 封装响应信息 */public class Response2 {    //两个常量    public static final String CRLF = "\r\n";    public static final String BLANK = " ";    //流    private BufferedWriter bw;    //正文    private StringBuilder content;    //存储头信息    private StringBuilder headInfo;    //存储正文长度    private int len = 0;    public Response2(){        headInfo = new StringBuilder();        content = new StringBuilder();        len = 0;    }    public Response2(OutputStream os){        this();        bw = new BufferedWriter(new OutputStreamWriter(os));    }    public Response2(Socket client){        this();        try {            bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));        } catch (IOException e) {            headInfo = null;        }    }    /**     * 构建正文     */    public Response2 print(String info){        content.append(info);        len += (info + CRLF).getBytes().length;        return this;    }    /**     * 构建正文+回车     */    public Response2 println(String info){        content.append(info).append(CRLF);        len += (info + CRLF).getBytes().length;        return this;    }    /**     * 构建响应头     */    private void createHeadInfo(int code){        //1)HTTP协议版本、状态代码、描述        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);        //2)响应头(Response Head)        headInfo.append("Server:test Server/0.0.1").append(CRLF);        headInfo.append("Date:").append(new Date()).append(CRLF);        headInfo.append("Content-type:text/html;charset=GBK").append(CRLF);        //正文长度:字节长度        headInfo.append("Content-type:").append(len).append(CRLF);        headInfo.append(CRLF);    }    /**     * 推送到客户端     * @throws IOException      */    public void pushToClient(int code) throws IOException{        if(null==headInfo){            code = 500;        }        createHeadInfo(code);        //头信息+分割符        bw.append(headInfo.toString());        //正文        bw.append(content.toString());        bw.flush();        bw.close();    }}

二、封装不同的小服务器Servlet,利用多态,这样就可以通过父类去调用子类

import java.io.IOException;public abstract class Servlet2 {    public void service(Request2 req,Response2 re) throws IOException{        doGet(req,re);        doPost(req,re);    }    public abstract void doGet(Request2 req,Response2 re) throws IOException;    public abstract void doPost(Request2 req,Response2 re) throws IOException;}
package cn.feng.http_2;import java.io.IOException;public class CopyOfLoginServlet extends Servlet2{    @Override    public void doGet(Request2 req, Response2 re) throws IOException {        String name = req.getParameterValue("uname");        String pwd = req.getParameterValue("pwd");        if(login(name, pwd)){            re.println("登录成功");        }else{            re.println("登录失败");        }    }    private boolean login(String name,String pwd){        return (name.equals("123"))&&(pwd.equals("456"));    }    @Override    public void doPost(Request2 req, Response2 re) throws IOException {    }}
package cn.feng.http_2;import java.io.IOException;public class RegisterServlet extends Servlet2{    @Override    public void doGet(Request2 req, Response2 re) throws IOException {    }    @Override    public void doPost(Request2 req, Response2 re) throws IOException {        re.println("<html><head><title>返回注册</title>");        re.println("</head><body>");        re.println("你的用户名为").println(req.getParameterValue("uname"));        re.println("</body></html>");    }}

三、主要工作:利用Map键与值之间的关系和多态来实现可以根据不同的请求实现不同的Servlet

package cn.feng.http_2;import java.util.HashMap;import java.util.Map;public class ServletContext {    //为每一个Servlet取一个别名    //如:login-->LoginServlet,也就是本来可以通过login来调用LoginServlet    private Map<String,Servlet2> servlet;    //表示映射,也就是为login取一个别名  也就是url-->login    //这样就可以通过很多个路径来获取login,因为Map的值是可以重复的    private Map<String,String> mapping;    public ServletContext() {        servlet = new HashMap<>();        mapping = new HashMap<>();    }    public Map<String, Servlet2> getServlet() {        return servlet;    }    public void setServlet(Map<String, Servlet2> servlet) {        this.servlet = servlet;    }    public Map<String, String> getMapping() {        return mapping;    }    public void setMapping(Map<String, String> mapping) {        this.mapping = mapping;    }}
package cn.feng.http_2;import java.util.Map;import cn.feng.http_1.Servlet;public class WebApp {    private static ServletContext contxt;    static{        contxt = new ServletContext();        Map<String,String> mapping = contxt.getMapping();        mapping.put("/login", "login");        mapping.put("/log", "login");        mapping.put("/reg", "register");        Map<String,Servlet2> servlet = contxt.getServlet();        servlet.put("login", new CopyOfLoginServlet());        servlet.put("register", new RegisterServlet());    }    public static Servlet2 getServlet(String url){        if(null==url || (url=url.trim()).equals("")){            return null;        }        return contxt.getServlet().get(contxt.getMapping().get(url));    }}

四、利用多线程实现对Servlet的调用

package cn.feng.http_2;import java.io.IOException;import java.net.Socket;public class Dispacher2 implements Runnable{    private Request2 req;    private Response2 re;    private Socket soc;    private int code=200;    public Dispacher2(Socket soc) {        this.soc = soc;        try {            req = new Request2(soc.getInputStream());            re = new Response2(soc);        } catch (IOException e) {//          e.printStackTrace();            code = 500;            return ;        }    }    @Override    public void run() {        try {            //利用多态得到相应的Servlet,可以理解为Servlet2 ser = LoginServlet,或者Servlet2 ser = RegisterServlet            //然后下面就是执行不用Servlet的功能            Servlet2 ser = WebApp.getServlet(req.getUrl());             if(null==ser){                code = 404;            }else{                ser.service(req, re);            }            re.pushToClient(code);        } catch (IOException e) { //          e.printStackTrace();            this.code = 500;            try {                re.pushToClient(code);            } catch (IOException e1) {//          e1.printStackTrace();            }        }        try {            soc.close();        } catch (IOException e) {            e.printStackTrace();        }    }}
package cn.feng.http_2;import java.io.IOException;import java.net.ServerSocket;public class MyServer2 {    private ServerSocket server ;    private static final String CRLF = "\r\n";    private static final String blank = " ";    private boolean flag = true;    public static void main(String[] args) {        new MyServer2().start();    }    public void start(){        start(10001);    }    public void start(int port){        try {            server = new ServerSocket(port);            receive();        } catch (IOException e) {//          e.printStackTrace();            stop();        }    }    public void receive(){        try {            while(flag){                new Thread(new Dispacher2(server.accept())).start();            }        } catch (IOException e) {//          e.printStackTrace();            stop();        }    }    public void stop(){        try {            flag = false;            server.close();        } catch (IOException e) {            e.printStackTrace();        }    }}
原创粉丝点击