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

手写服务器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();
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在线IOS免签封包分发平台一键IOS免签支持在线封装app分发源码 所有功能可进行二次开发,如有需要可联系客服进行详细沟通~ 主要功能: 1、一键IOS免签封装; 2、免签IOS,自动生成下载二维码; 3、免签生成的IOS可与APK合并二码合一 3、支持三方免签支付码支付、支持七牛云存储; PS:源码不断升级优化,UI、功能均以演示站点为准,购买前请体验好功能!没有完美的源码,所售源码保证和演示站一样,请看演示再购买。 工作原理:直接调用苹果自带的Safari浏览打开客户的H5网址的、所以也不会存在客户目标网站域名在微信或QQ打不开的问题以及不会掉签问题。 基于目前主流的分发平台系统二开而来 1.本程序支持安卓和苹果分发,上传后自动判断,通过技术手段已经让IOS和安卓用户稳定安装 2.智能提取APP应用信息,自动生成IOS应用PLIST,开发信息,方便用户在测试。 3.支持阿里云和七牛云存储绑定。 充值点数比例固定默认比例是一比一百点。 对接码支付版的分发系统,修复官方后门与修复数据库被删除的后门 新增许多功能,功能完善. 新增会员容量 新增实名认证 活动促销中 与市面上所有版本完全不一样。 纠正iOS应用封装后图标不变的问题(部分应用不显示图标) 修正一个安装iOS应用时立即信任的错误… 后台充值记录秘钥购买记录暂无,不影响使用。 应用封装(具体效果需用户自行体验)不支持安卓封装 新增应用广告功能 设备识别功能优化 短链接修复 一系列优化与修正…

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值