手写一个基于BIO的Java服务器

1.项目架构

 2.启动类

        在启动类中初始化配置信息和线程池信息,然后创建ServerSocket并使用socket.accept()阻塞等待http请求。

public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        try {
            bootStrap.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void start() throws Exception {
        //初始化servlet
        loadServlet();
        //初始化server配置
        loadServer();
//        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 15, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10));
        //初始化线程池
        ThreadPoolExecutor poolExecutor = ThreadPollConfig.getThreadPool(parameterConfig);
        //创建socket监听端口
        ServerSocket serverSocket = new ServerSocket(port);

        System.out.println("服务器启动,绑定" + port + "端口");
        while (true) {
            synchronized (lock) {
                //监听
                Socket socket = serverSocket.accept();
                //处理socket
                RequestProcessor requestProcessor = new RequestProcessor(socket, httpServletMap);
                poolExecutor.execute(requestProcessor);
            }
        }
    }

 3.请求处理

        该部分负责封装Request对象和Response对象,并判断请求路径是动态资源还是静态资源,来使用对应的类进行处理。

public void run() {
        try {
            //监听端口
//            System.out.println("线程" + Thread.currentThread().getName() + "收到请求");
            //接收请求流
            InputStream inputStream = socket.getInputStream();
            //处理请求流数据,封装成request对象
            Request request = handleRequest(inputStream);
            if (request == null) return;
            //处理输出流数据,封装成response对象
            OutputStream outputStream = socket.getOutputStream();
            Response response = new Response(outputStream);
            String url = request.getUrl();
            //判断请求路径是否有servlet来匹配
            String match = match(url);
            if (!"".equals(match)) {
                //交给对应的servlet来处理
                HttpServlet httpServlet = httpServletMap.get(match);
                httpServlet.init();
                httpServlet.service(request, response);
                httpServlet.destroy();
            } else {
                //处理静态资源
                response.writeResource(url);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            try {
                socket.close();
//                System.out.println("线程" + Thread.currentThread().getName() + "socket已关闭");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

4.处理动态资源

        创建抽象类Servlet和HttpServlet类

public interface Servlet {
    void init() throws Exception;
    void destroy() throws Exception;
    void service(Request request, Response response) throws Exception;
}
public abstract class HttpServlet implements Servlet {

    protected abstract void doGet(Request request, Response response);

    protected abstract void doPost(Request request, Response response);

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void service(Request request, Response response) {
        //请求分发
        if ("GET".equals(request.getMethod())) doGet(request, response);
        else doPost(request, response);
    }
}

        自定义Servlet类继承HttpServlet类并重写doGet和doPost方法。 

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(Request request, Response response) {
        try {
            response.writeAndFlush("MyServlet--get请求" + request.getUrl());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void doPost(Request request, Response response) {
        try {
            response.writeAndFlush("MyServlet--post请求");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

5.处理静态资源

        解析文件的绝对路径并判断文件是否存在

public void writeResource(String url) throws IOException {
        //获取文件真实路径
        String path = this.getClass().getResource("/").getPath() + url;
        //判断是否存在该文件
        File file = new File(path);
        if (file.exists() && file.isFile()) {
            String suffix = path.substring(path.lastIndexOf('.'));
            //解析文件
            DefaultResourceResolver defaultResourceResolver = DefaultResourceResolver.getInstance();
            defaultResourceResolver.resolve(file, this, SuffixResolver.suffixResolver(suffix));
        }else {
            write404();
        }
    }

        使用流解析文件,注意outputStream不能在此处关闭,否则会将socket的资源一起释放

public void resolve(File file, Response response, String contentType) throws IOException {
        InputStream inputStream = Files.newInputStream(file.toPath());
        int len = 0;
        while (len == 0) {
            len = inputStream.available();
        }
        //先写入请求头
        ResponseHeader responseHeader = new ResponseHeader.Builder()
                .statusCode(HttpStatusCode.SUCCESS)
                .contentType(contentType)
                .contentLength(String.valueOf(len))
                .build();

        response.write(responseHeader.toString());
        OutputStream outputStream = response.getOutputStream();

        byte[] bytes = new byte[bufferSize];
        while ((len = inputStream.read(bytes)) != -1) {
            //每次读取的数据都实时写入response中
            if (len < bufferSize) {
                for (int i = 0; i < len; i++) {
                    outputStream.write(bytes[i]);
                }
            } else {
                outputStream.write(bytes);
            }
        }
        inputStream.close();
        response.flush();
    }

6.结果图

        

 

7.注意

        所有的response必须先写入请求头信息,否则无法成功在浏览器响应。

8.代码地址:https://gitee.com/cg--cg/framework

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值