WebServer-简易服务器

本文目录

  • 简易服务器(WebServer)的搭建
    • 简介
    • 步骤和代码
          • 1.创建服务器WebServer,通过socket传输数据,threadPool线程池管理线程
          • 2.新建线程管理实现Runnable接口,负责管理连接后的线程
          • 3.HttpRequest负责处理客户端发送过来的请求,并且解析请求参数
          • 4.HttpResponse 负责对客户端的响应,处理对应的请求
          • 5.servlet设计实例:两个简单的servlet,注册和登陆
          • 6.其他功能
            • 6.1 配置文件context
            • 6.2 自定义异常exception
            • 6.3 关于访问路径
    • 源码下载

简易服务器(WebServer)的搭建

简介

本项目以http协议的一问一答方式为基础,通过socket来处理服务器和客户端之间的请求;本项目为某机构学习所得,此文记录自己的程序员学习之路;(侵权即删)

步骤和代码

1.创建服务器WebServer,通过socket传输数据,threadPool线程池管理线程
**
 *  简易服务器,通过socket进行服务器和客户端之间的交流
 */
public class WebServer {

    private ServerSocket serverSocket;
    private ExecutorService threadPool;


    public WebServer() {

        try {

            System.out.println("正在启动服务器...");
            serverSocket = new ServerSocket(8088);
            System.out.println("服务器启动成功...");

            threadPool = Executors.newFixedThreadPool(50);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    private void start() {
        try {
            while (true) {
                System.out.println("正在连接客户端...");
                Socket socket = serverSocket.accept();
                System.out.println("客户端连接成功");
				
				// 客户端连接后通过线程进行管理
                ClinetHandler handler = new ClinetHandler(socket);

                threadPool.execute(handler);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    public static void main(String[] args) {

        WebServer webServer = new WebServer();
        webServer.start();
    }
}
2.新建线程管理实现Runnable接口,负责管理连接后的线程
**
 * 每次连接一个客户端服务器都会在这里处理与客户端链接
 */
public class ClinetHandler implements Runnable {
    private Socket socket;

    public ClinetHandler(Socket socket) {

        this.socket = socket;
    }

    public void run() {
        try {

            //1.解析请求
            HttpRequest request = new HttpRequest(socket);

            //2.处理请求
            HttpResponse response = new HttpResponse(socket);
            String fileName = request.getRequestURI();


            HttpServlet servlet = ServletContext.getServletMapping(fileName);
            if (servlet != null) {
                servlet.services(request, response);
            } else {
                String path = "./src/main/webapp" + fileName;
                File entity = new File(path);
                if (entity.exists() && entity.isFile()) {
                    response.setEntity(entity);
                } else {
                    response.setStatusCode(404);
                    response.setStatusReason("NOT FOUND PAGE");
                    response.setEntity(new File("./src/main/webapp/root/notfound.html"));
                }
            }
            //3.发送响应
            response.flush();
            System.out.println("发送响应完毕");
        } catch (EmptyRequestException e1) {
            System.out.println("------------->这是一个空请求");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}
3.HttpRequest负责处理客户端发送过来的请求,并且解析请求参数
**
 * 请求类,接受请求,处理请求
 */
public class HttpRequest {
    private Socket socket;

    //请求行的参数
    private String requestMethod;
    private String requestURL;
    private String protocol;
    //业务请求的参数
    private String requestURI;
    private Map requestParam = new HashMap<>();
    //消息头参数
    private Map headParam = new HashMap();

    public HttpRequest(Socket socket) throws EmptyRequestException {
        try {
            this.socket = socket;
            parseRequest();
        } catch (EmptyRequestException e) {
            throw e;
        }

    }

    //解析请求
    private void parseRequest() throws EmptyRequestException {
        try {
            parseRequestLine();
            parseRequestHead();
            parseRequestContext();
        } catch (EmptyRequestException e) {
            throw e;
        }

    }

    //解析请求行
    private void parseRequestLine() throws EmptyRequestException {

        try {
            System.out.println("开始解析请求行..");
            String line = readLine();
            if ("".equals(line)) {
                throw new EmptyRequestException("空请求");
            }
            System.out.println("请求行:" + line);
            String[] data = line.split("\\s");
            requestMethod = data[0];
            requestURL = data[1];
            if (requestURL.contains("?")) {
                parseRequestUrl(requestURL);
            } else {
                requestURI = requestURL;
            }

            protocol = data[2];
            System.out.println("请求参数--method:" + requestMethod + " url: " + requestURL + " protocol:" + protocol);

            System.out.println("解析请求行完成!");

        } catch (EmptyRequestException e1) {
            throw e1;
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void parseRequestUrl(String URL) {
        String[] data = requestURL.split("\\?");
        if (data.length > 1) {
            String[] params = data[1].split("&");
            for (String s : params) {
                String[] param = s.split("=");
                if (param.length > 1) {
                    requestParam.put(param[0], param[1]);
                } else {
                    requestParam.put(param[0], null);
                }
            }
        }
        requestURI = data[0];
    }

    //解析消息头
    private void parseRequestHead() {

        try {
            System.out.println("开始解析消息头..");
            String line = null;
            while (!"".equals(line = readLine())) {
                String[] data = line.split(": ");
                headParam.put(data[0], data[1]);
            }
            Traverse.traverStringMap(headParam.entrySet());
            System.out.println("解析消息头完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    //解析消息正文
    private void parseRequestContext() {
        try {
            System.out.println("开始解析消息正文..");

            System.out.println("解析消息正文完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private String readLine() throws IOException {
        InputStream in = socket.getInputStream();
        StringBuilder builder = new StringBuilder();
        int r1 = -1;
        int r2 = 0;
        while ((r1 = in.read()) != -1) {
            if (r1 == 10 && r2 == 13) {
                break;
            }
            builder.append((char) r1);
            r2 = r1;
        }
        return builder.toString().trim();
    }

    public String getRequestMethod() {
        return requestMethod;
    }

    public void setRequestMethod(String requestMethod) {
        this.requestMethod = requestMethod;
    }

    public String getRequestURL() {
        return requestURL;
    }

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public String getHeadParam(String paramkey) {
        return headParam.get(paramkey);
    }

    public void setHeadParam(Map headParam) {
        this.headParam = headParam;
    }

    public String getRequestParam(String key) {
        return requestParam.get(key);
    }

    public String getRequestURI() {
        return requestURI;
    }

    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }
}
4.HttpResponse 负责对客户端的响应,处理对应的请求
**
 * 响应类,根据请求和业务发送的响应
 */
public class HttpResponse {

    private Socket socket;
    private OutputStream out;
    //响应状态行
    private String protocol = "HTTP/1.1";
    private int statusCode = 200;
    private String statusReason = "OK";

    private File entity;

    //响应头
    private Map responseHeadParam = new HashMap<>();

    public HttpResponse(Socket socket) {

        this.socket = socket;
        try {
            out = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void flush() {
        flushResponseStatus();
        flushResponseHead();
        flushResponseContext();
    }

    /**
     * 响应状态行
     */
    private void flushResponseStatus() {

        try {
            System.out.println("准备发送状态行..");
            String statusLine = protocol + " " + statusCode + " " + statusReason;
            System.out.println("状态行:  " + statusLine);
            out.write(statusLine.getBytes("ISO8859-1"));

            out.write(13);//CR
            out.write(10);//LF
            System.out.println("发生状态行完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 发送响应头
     */
    private void flushResponseHead() {
        try {
            System.out.println("准备发送响应头..");

            Set> entries = responseHeadParam.entrySet();
            for (Map.Entry e : entries) {
                String line = e.getKey() + ": " + e.getValue();
                out.write(line.getBytes("ISO8859-1"));
                out.write(13);
                out.write(10);
            }

            out.write(13);
            out.write(10);
            System.out.println("发送响应头完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 发送响应正文
     */
    private void flushResponseContext() {
        try {
            System.out.println("准备发送响应正文...");
            FileInputStream in = new FileInputStream(entity);
            System.out.println("响应正文: " + entity.getName());

            byte[] data = new byte[1024];
            int len = -1;
            while ((len = in.read(data)) != -1) {
                out.write(data, 0, len);
            }
            System.out.println("发送响应正文完毕!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(
            int statusCode) {
        this.statusCode = statusCode;
    }

    public String getStatusReason() {
        return statusReason;
    }

    public void setStatusReason(String statusReason) {
        this.statusReason = statusReason;
    }

    public File getEntity() {
        return entity;
    }

    public void setEntity(File entity) {
        this.entity = entity;
        String ext = entity.getName().substring(entity.getName().lastIndexOf(".") + 1);
        String type = HttpContext.getTypeMapping(ext);
        responseHeadParam.put("Content-Type", type);
        responseHeadParam.put("Content-Length", String.valueOf(entity.length()));
    }
5.servlet设计实例:两个简单的servlet,注册和登陆
**
 * 注册业务类
 */
public class HttpRegServlet extends HttpServlet {
    /**
     * 注册用户
     *
     * @param request
     * @param response
     */
    public void services(HttpRequest request, HttpResponse response) {

        String username = request.getRequestParam("username");
        String password = request.getRequestParam("password");
        Integer age = Integer.parseInt(request.getRequestParam("age"));
        String nicknane = request.getRequestParam("nickname");
        File file = null;
        if (username == null || password == null || nicknane == null || age == null) {
            forward(request, response, "/myweb/reg_fail.html");
            return;
        }

        try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw")) {

            //判断用户是否存在
            for (int i = 0; i < raf.length(); i += 100) {
                raf.seek(i);
                byte[] data = new byte[32];
                raf.read(data);
                String oldNames = new String(data, "UTF-8").trim();
                if (oldNames.equals(username)) {
                    forward(request, response, "/myweb/reg_fail.html");
                    return;
                }
            }
            //新增用户
            raf.seek(raf.length());
            byte[] data = username.getBytes("UTF-8");
            data = Arrays.copyOf(data, 32);
            raf.write(data);

            data = password.getBytes("UTF-8");
            data = Arrays.copyOf(data, 32);
            raf.write(data);

            data = nicknane.getBytes("UTF-8");
            data = Arrays.copyOf(data, 32);
            raf.write(data);

            raf.writeInt(age);
            forward(request, response, "/myweb/reg_success.html");
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}
/**
 * 用户登录
 */
public class HttpLoginServlet extends HttpServlet {

    public void services(HttpRequest request, HttpResponse response) {

        String username = request.getRequestParam("username");
        String password = request.getRequestParam("password");
        File file = null;
        if (username == null || password == null) {
            forward(request, response, "/myweb/login_fail.html");
            return;
        }
        try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw")) {

            for (int i = 0; i < raf.length(); i += 100) {

                raf.seek(i);
                byte[] data = new byte[32];
                raf.read(data);
                String oldName = new String(data, "UTF-8").trim();
                if (oldName.equals(username)) {
                    raf.seek(i + 32);
                    data = new byte[32];
                    raf.read(data);
                    String oldPassword = new String(data, "UTF-8").trim();
                    if (oldPassword.equals(password)) {
                        forward(request, response, "/myweb/login_success.html");
                        return;
                    }
                }
            }
            forward(request, response, "/myweb/login_fail.html");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * servlet的父类,用于统一servlet的基本功能
 */
public abstract class HttpServlet {

    /**
     * 业务请求
     *
     * @param request
     * @param response
     */
    public abstract void services(HttpRequest request, HttpResponse response);

    /**
     * 发送响应
     *
     * @param request
     * @param response
     * @param filePath
     */
    public void forward(HttpRequest request, HttpResponse response, String filePath) {
        File file = new File("./src/main/webapp" + filePath);
        response.setEntity(file);
    }


6.其他功能
6.1 配置文件context

HttpContext:将tomcat中文件后缀名对应的网络请求中需要发送的Content-Type初始化对应
ServletContext:储存Servlet和请求路径的对应关于,通过反射来调用

6.2 自定义异常exception

自行定义了一个网络请求中的空请求异常EmptyRequestException,亦可自行设计其他异常

6.3 关于访问路径

启动项目,在浏览器输入:http://localhost:8088/+“自己项目文件夹”(localhost也可以是本机ip,8088在webserver中自行定义,要注意端口号冲突)
此项目 登录:http://localhost:8088/myweb/login.html
此项目 注册: http://localhost:8088/myweb/reg.html (需要自行启动此项目才可访问)

源码下载

点击下载源码(提取码:3ohr)

你可能感兴趣的:(WebServer-简易服务器)