java 手写服务器

html

<html>
    <head>
        <title>表单</title>
    </head>
    <body>
        <form method="post" action="http://localhost:8888">
            <!--id常用在js中,用于前端区分唯一性;name常用在后端区分唯一性-->
            用户名:<input type="text" name="username" id="username"/>
            密码:<input type="password" name="password" id="password"/>
            <input type="submit" value="登录">
        </form>
    </body>
</html>

http协议

HTTP请求头和响应头含义

1、请求(客户端->服务端[request])
    GET(请求的方式) /books/java.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
    Accept: */*(客户端能接收的资源类型)
    Accept-Language: en-us(客户端接收的语言类型)
    Connection: Keep-Alive(维护客户端和服务端的连接关系)
    Host: localhost:8080(连接的目标主机和端口号)
    Referer: http://localhost/links.asp(从来于哪里)
    User-Agent: Mozilla/4.0(客户端版本号的名字)
    Accept-Encoding: gzip, deflate(客户端能接收的压缩数据的类型)
    If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(缓存时间) 
    Cookie(客户端暂存服务端的信息)
    Date: Tue, 11 Jul 2000 18:23:51 GMT(客户端请求服务端的时间)

2、响应(服务端->客户端[response])
    HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
    302(客户端请求服务端,但服务端没有对应的资源,服务端要客户端再次请求找其它的服务端,即客户端二次请求,重定向) 
    307(客户端请求服务端,但服务端没有对应的资源,服务端自行再次请求找其它的服务端,即客户端一次请求,转发)
    304(客户端请求服务端,此时客户端缓存中有,无需再从服务端下载新的内容,服务端叫客户端自行找缓存,优化)
    500(客户端请求的资源,服务端存在,但在执行时出错)
    Location: http://www.baidu.com(服务端需要客户端访问的页面路径) 
    Server:apache tomcat(服务端的Web服务端名)
    Content-Encoding: gzip(服务端能够发送压缩编码类型) 
    Content-Length: 80(服务端发送的压缩数据的长度) 
    Content-Language: zh-cn(服务端发送的语言类型) 
    Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
    Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
    Refresh: 1;url=http://www.it315.org(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
    Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
    Transfer-Encoding: chunked(分块传递数据到客户端)  
    Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
    Expires: -1//3种(服务端禁止客户端缓存页面数据)
    Cache-Control: no-cache(服务端禁止客户端缓存页面数据)  
    Pragma: no-cache(服务端禁止客户端缓存页面数据)   
    Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)  
    Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)

httpserver

项目结构

/**
 * 创建服务器,并启动 
 * 1、请求 
 * 2、响应
 * @author L J
 */
public class Server {
    private ServerSocket server;
    //是否停止服务
    private boolean isShutDown;

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    /**
     * 启动服务器方法
     */
    public void start() {
        start(8888);
    }

    /**
     * 启动服务器方法
     */
    public void start(int port) {
        try {
            server = new ServerSocket(port);
            this.receive();
        } catch (IOException e) {
            stop();
        }
    }

    /**
     * 接收客户端
     */
    private void receive() {
        try {
            while(!isShutDown) {
                new Thread(new Dispatcher(server.accept())).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 停止服务器方法
     */
    public void stop() {
        isShutDown = true;
        CloseUtil.close(server);
    }
}
/**
 * 封装Request 
 * @author L J
 */
public class Request {
    //请求方式
    private String method;
    //请求资源
    private String url;
    //请求参数
    private Map<String, List<String>> parameterValue;
    //换行符常量
    public static final String CRLF = "\r\n";
    //输入流
    private InputStream is;
    //请求信息
    private String requestInfo;

    public Request() {
        method = "";
        url = "";
        parameterValue = new HashMap<String, List<String>>();
    }

    public Request(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();
    }

    /**
     * 分析请求头信息
     */
    private void parseRequestInfo() {
        if(requestInfo == null || (requestInfo = requestInfo.trim()).equals("")) {
            return;
        }

        /**
         * 思路
         * =======================================
         * 从头信息中分析出:请求方式  请求路径  请求参数(可能不存在)
         * 如果是POST请求方式,请求参数可能在最后的正文中
         * =======================================
         */

        //接收请求参数
        String paramString = "";

        //获取请求方式
        String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF));
        int index = requestInfo.indexOf("/"); //  /的位置
        this.method = firstLine.substring(0, index).trim();
        String urlStr = firstLine.substring(index, firstLine.indexOf("HTTP/1.1")).trim();
        //只考虑get和post请求方式
        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;
            }
        }

        //不存在请求参数
        if(paramString.equals("")) {
            return;
        }

        //将请求参数封装到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("=");

            //如果有参数没有值,将值设为null
            if(keyValues.length == 1){
                keyValues = Arrays.copyOf(keyValues, 2);
                keyValues[1] = null;
            }

            String key = keyValues[0].trim();
            String value = keyValues[1] == null ? null : decode(keyValues[1].trim(), "utf-8");

            //转换成Map 分拣存储
            if(!parameterValue.containsKey(key)) {
                parameterValue.put(key, new ArrayList<String>());
            }
            List<String> values = parameterValue.get(key);
            values.add(value);
        } 
    }

    /**
     * 根据页面的name获取对应的多个值
     * @param name
     * @return
     */
    public String[] getParameterValues(String name) {
        List<String> values = null;
        if((values=parameterValue.get(name)) == null) {
            return null;
        }else{
            return values.toArray(new String[0]);
        }
    }

    /**
     * 根据页面的name获取对应的一个值
     * @param name
     * @return
     */
    public String getParameter(String name) {
        String[] values = getParameterValues(name);
        if(values == null) {
            return null;
        }
        return values[0];
    }

    //解码,解决中文乱码问题
    private String decode(String value, String code) {
        try {
            return URLDecoder.decode(value, code);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getUrl() {
        return url;
    }
}
/**
 * 封装响应信息
 * @author L J
 */
public class Response {
    //存储响应头信息
    private StringBuilder headInfo;
    //存储响应正文信息
    private StringBuilder content;
    //输出流
    private BufferedWriter bw;
    //换行符常量
    private static final String CRLF = "\r\n";
    //空格常量
    private static final String BLANK = " ";
    //存储正文长度
    private int len = 0;

    public Response() {
        headInfo = new StringBuilder();
        content = new StringBuilder();
        len = 0;
    }

    public Response(OutputStream os) {
        this();
        bw = new BufferedWriter(new OutputStreamWriter(os));
    }

    public Response(Socket client) {
        this();
        try {
            bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        } catch (IOException e) {
            headInfo = null;
        }
    }

    /**
     * 构建正文
     * @param info
     * @return
     */
    public Response print(String info) {
        content.append(info);
        len += info.getBytes().length;
        return this;
    }

    /**
     * 构建正文+回车
     * @param info
     * @return
     */
    public Response 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)响应头
        headInfo.append("Server:bjsxt 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-Length:").append(len).append(CRLF);
        // 3)正文之前
        headInfo.append(CRLF);
    }

    /**
     * 推送到客户端
     * @throws IOException 
     */
    void pushToClient(int code) throws IOException {
        if(headInfo == null) {
            code = 500;
        }
        createHeadInfo(code);
        //头信息+分隔符
        bw.append(headInfo.toString());
        //正文
        bw.append(content.toString());
        bw.flush();
        CloseUtil.close(bw);
    }
}
/**
 * 一个请求与一个响应  就一个此对象
 * @author L J
 */
public class Dispatcher implements Runnable{
    private Socket client;
    //请求
    private Request req;
    //响应
    private Response res;
    //状态码
    private int code;

    public Dispatcher(Socket client) {
        this.client = client;
        try {
            req = new Request(client.getInputStream());
            res = new Response(client.getOutputStream());
        } catch (IOException e) {
            code = 500;
            return;
        }
    }

    @Override
    public void run() {
        try {
            Servlet servlet = WebApp.getServlet(req.getUrl());
            if(servlet == null) {
                this.code = 404;
            }else{
                //调用服务
                servlet.service(req, res);
            }
            //将响应信息输出到客户端
            res.pushToClient(code);
        } catch (Exception e) {
            this.code = 500;
        }
        //关闭socket
        CloseUtil.close(client);
    }
}
/**
 * 上下文容器
 * @author L J
 */
public class ServletContext {
    //为每一个Servlet取一个别名,一个Servlet可以有多条路径进行访问
    //login-->com.belief.server.LoginServlet
    private Map<String, String> servlet;
    //映射  /log-->login /login-->login
    private Map<String, String> mapping;

    public ServletContext() {
        servlet = new HashMap<String, String>();
        mapping = new HashMap<String, String>();
    }

    public Map<String, String> getServlet() {
        return servlet;
    }
    public void setServlet(Map<String, String> servlet) {
        this.servlet = servlet;
    }
    public Map<String, String> getMapping() {
        return mapping;
    }
    public void setMapping(Map<String, String> mapping) {
        this.mapping = mapping;
    }
}
/**
 * 存储:
 * <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.belief.server.LoginServlet</servlet-class>
    </servlet>
 * @author L J
 */
public class Entity {
    //Servlet的别名
    private String name;
    //Servlet的路径
    private String clz;

    public Entity() {
    }

    public Entity(String name, String clz) {
        this.name = name;
        this.clz = clz;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClz() {
        return clz;
    }

    public void setClz(String clz) {
        this.clz = clz;
    }
}
/**
 * 存储:
 * <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
 * @author L J
 */
public class Mapping {
    //Servlet的别名
    private String name;
    //要访问Servlet的url
    private List<String> urlPattern;

    public Mapping() {
        urlPattern = new ArrayList<String>();
    }

    public Mapping(String name, List<String> urlPattern) {
        this.name = name;
        this.urlPattern = urlPattern;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getUrlPattern() {
        return urlPattern;
    }

    public void setUrlPattern(List<String> urlPattern) {
        this.urlPattern = urlPattern;
    }
}
//SAX解析web.xml文件
public class WebHandler extends DefaultHandler{
    //存储解析出来的Entity
    private List<Entity> entityList;
    //存储解析出来的Mapping
    private List<Mapping> mappingList;
    //Entity实体--><servlet></servlet>标签
    private Entity entity;
    //Mapping实体--><servlet-mapping></servlet-mapping>标签
    private Mapping mapping;
    //记录开始标签
    private String beginTag;
    //是否是在解析servlet-mapping
    private boolean isMap;

    @Override
    public void startDocument() throws SAXException {
        //文档解析开始
        entityList = new ArrayList<Entity>();
        mappingList = new ArrayList<Mapping>();
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        //开始元素
        if(qName != null) {
            beginTag = qName;
            if(qName.equals("servlet")) {
                isMap = false;
                entity = new Entity();
            }else if(qName.equals("servlet-mapping")) {
                isMap = true;
                mapping = new Mapping();
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        //处理内容
        if(beginTag != null) {
            String str = new String(ch, start, length);
            if(isMap) {
                if(beginTag.equals("servlet-name")) {
                    mapping.setName(str);
                }else if(beginTag.equals("url-pattern")) {
                    mapping.getUrlPattern().add(str);
                }
            }else{
                if(beginTag.equals("servlet-name")) {
                    entity.setName(str);
                }else if(beginTag.equals("servlet-class")) {
                    entity.setClz(str);
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName)
    throws SAXException {
        //结束元素
        if(qName != null) {
            if(qName.equals("servlet")) {
                entityList.add(entity);
            }else if(qName.equals("servlet-mapping")) {
                mappingList.add(mapping);
            }
        }
        beginTag = null;
    }

    @Override
    public void endDocument() throws SAXException {
        //文档解析结束
    }

    public List<Entity> getEntityList() {
        return entityList;
    }

    public void setEntityList(List<Entity> entityList) {
        this.entityList = entityList;
    }

    public List<Mapping> getMappingList() {
        return mappingList;
    }

    public void setMappingList(List<Mapping> mappingList) {
        this.mappingList = mappingList;
    }
}
//模拟应用
public class WebApp {
    private static ServletContext context;
    static {
        try {
            //获取解析工厂
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //获取解析器
            SAXParser sax = factory.newSAXParser();
            //指定xml+处理器
            WebHandler web = new WebHandler();
            sax.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("WEB-INF/web.xml"), web);

            context = new ServletContext();

            //将List转成Map
            Map<String,String> mapp = context.getMapping();
            Map<String,String> servlet = context.getServlet();

            //servlet-name  servlet-class
            for(Entity entity : web.getEntityList()) {
                servlet.put(entity.getName(), entity.getClz());
            }

            //url-pattern  servlet-name
            for(Mapping mapping : web.getMappingList()) {
                List<String> urls = mapping.getUrlPattern();
                for(String url : urls) {
                    mapp.put(url, mapping.getName());
                }
            }
        } catch (Exception e) {

        }
    }

    public static Servlet getServlet(String url) throws Exception {
        //如果没有url或url==null
        if(url == null && (url = url.trim()).equals("")) {
            return null;
        }

        //利用反射根据字符串(完整路径)创建对象
        //return context.getServlet().get(context.getMapping().get(url));
        String name = context.getServlet().get(context.getMapping().get(url));
        return (Servlet)Class.forName(name).newInstance();
    }
}
/**
 * 关闭流工具类
 * 
 * @author L J
 */
public class CloseUtil {
    // 关闭流
    public static void close(Closeable... io) {
        for (Closeable temp : io) {
            if (temp != null) {
                try {
                    temp.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 关闭Socket
    public static void close(Socket socket) {
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //关闭ServerSocket
    public static void close(ServerSocket serverSocket) {
        try {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public abstract class Servlet {
    //服务端的处理
    public void service(Request req, Response res) throws Exception {
        this.doGet(req, res);
        this.doPost(req, res);
    }

    protected abstract void doGet(Request req, Response res) throws Exception;
    protected abstract void doPost(Request req, Response res) throws Exception;
}
//登录
public class LoginServlet extends Servlet{
    @Override
    public void doGet(Request req, Response res) throws Exception {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        boolean flag = login(username, password);
        if(flag) {
            res.println("登录成功!");
        }else{
            res.println("登录失败!");
        }
    }

    public boolean login(String username, String password) {
        return username.equals("哈哈") && password.equals("123456");
    }

    @Override
    public void doPost(Request req, Response res) throws Exception {

    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
    <!-- 登录 -->
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.belief.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
        <url-pattern>/log</url-pattern>
    </servlet-mapping>
    <!-- 注册 -->
    <servlet>
        <servlet-name>register</servlet-name>
        <servlet-class>com.belief.servlet.RegisterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>register</servlet-name>
        <url-pattern>/register</url-pattern>
    </servlet-mapping>
</web-app>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值