java:手写webserver

视频地址:http://www.bilibili.com/video/av59814573?p=252
粗略实现了视频的代码,没有完全按视频操作,如不读取xml,使用注解,不使用多线程等。
要点:

  1. 使用反射读取注解生成urlmapping的hashmap
  2. 使用反射根据请求的url选择响应的servlet
  3. 了解了request和response的操作
  4. response有严格的格式要求,比如响应体必须使用正确的html标签
//注解类,用来设置servlet处理的url地址的注解,不设置@Target或@Retention会失效
package slq;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
    String[] value();
}
//自定义myServlet接口
package slq;
public interface MyServlet {
    public void service(MyRequest q,MyResponse p);
}
//处理 /login***的Servlet 
package slq;
@MyController({"/login"})
public class LoginServlet implements  MyServlet {
    @Override
    public void service(MyRequest q, MyResponse p) {
        String username = q.getParameter("username");
        p.println("LoginServletWelcome ");
        p.println(username);
        p.println("!");
        p.pushToBrowser(200);
    }
}
package slq;
//处理 /reg***和 /register***的Servlet 
@MyController({"/reg","/register"})
public class RegServlet implements MyServlet {
    @Override
    public void service(MyRequest q, MyResponse p) {
        String username = q.getParameter("username");
        String password = q.getParameter("password");
        if(username.equals(password)){
            p.println("Regreg success");
            p.pushToBrowser(200);
        }else{
            p.println("Regreg failed");
            p.pushToBrowser(404);
        }
    }
}
//模拟request,只实现了获取parameter的功能
package slq;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;

public class MyRequest {
    private Socket client;
    public String uri;
    private HashMap<String, String> map = new HashMap();
    public MyRequest(Socket client) {
        this.client=client;
        InputStream is = null;
        try {
            is = client.getInputStream();
            byte[] data=new byte[1024];
            int len = is.read(data);
            String s = new String(data, 0, len);
            getParameterMap(s);
            this.uri=s.substring(s.indexOf("/")+1,s.indexOf("?"));
            //System.out.println(s+"-----"+this.uri);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getParameter(String key){
        return this.map.get(key);
    }

    public void getParameterMap(String s){
        String s1=s.substring(s.indexOf("?")+1,s.indexOf(" HTTP"));
        System.out.println("s1-->"+s1);
        String[] split = s1.split("&");
        for(String sp:split){
            String[] spl = sp.split("=");
            this.map.put(spl[0],spl[1]);
        }
    }
}
//模拟reponse,实现了写响应体,响应头的功能,重点是响应是怎么写进client的
package slq;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;

public class MyResponse {
    private Socket client;
    private StringBuilder body;
    private StringBuilder head;
    private BufferedWriter bw;
    private final String CTRF="\r\n";

    public MyResponse(Socket client) {
        this.client = client;
        this.body=new StringBuilder();
        this.head=new StringBuilder();
        try {
            this.bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void println(String s){
        body.append(s);
    }
    
    public void pushToBrowser(int code) {
        setHead(code);
        try {
            bw.write(head.toString());
            bw.write(body.toString());
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void setHead(int code){
        head.append("HTTP/1.0 ").append(code==200?"200 OK":"404 BAD").append(CTRF);
        head.append("Date:").append(new Date()).append(CTRF);
        head.append("Server:SLQ SERVER").append(CTRF);
        head.append("MINASE:INORI").append(CTRF);
        head.append("Content-type:text/html").append(CTRF);
        head.append("Content-length:").append(body.length()).append(CTRF).append(CTRF);
    }
}
//程序入口,用读取注解生成urlmapping的hashmap,然后根据请求的url选择响应的servlet,这两步都使用了反射
package slq;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class WebApp {
    public HashMap<String,String> mapping;
    public static void main(String[] args) throws Exception {
        WebApp app=new WebApp();
        app.run();
    }

    public WebApp() throws ClassNotFoundException {
        this.mapping=new HashMap();
        getMapByClassName("slq.LoginServlet");
        getMapByClassName("slq.RegServlet");           
        //{/reg=slq.RegServlet, /login=slq.LoginServlet, /register=slq.RegServlet}
        System.out.println(this.mapping);    
    }

    private void run() throws Exception {
        ServerSocket s=new ServerSocket(9090);
        while(true){
            Socket client = s.accept();
            MyRequest myRequest = new MyRequest(client);
            MyResponse myResponse = new MyResponse(client);
            String classname = mapping.get("/" + myRequest.uri);
            if(classname==null)}{
            	client.close();
            	continue;
            }
            Class<?> clz = Class.forName(classname);
            MyServlet servlet =(MyServlet) clz.getDeclaredConstructor().newInstance();
            servlet.service(myRequest,myResponse);
            client.close();
        }
    }

    public void getMapByClassName(String name) throws ClassNotFoundException {
        MyController anno = Class.forName(name).getAnnotation(MyController.class);
        for(String s:anno.value()){
            this.mapping.put(s,name);
        }
    }
}

一个测试例子:

post访问http://localhost:9090/login?username=311&password=310

响应体:

<html>
    <head>
        <title>LoginServlet</title>
    </head>
    <body>Welcome 311!</body>
</html>

响应头:

content-length →78
content-type →text/html
date →Mon Mar 16 22:48:42 CST 2020
minase →INORI
server →SLQ SERVER

你可能感兴趣的:(java相关)