从零手写实现 nginx-02-nginx 的核心能力

前言

大家好,我是老马。很高兴遇到你。

我们希望实现最简单的 http 服务信息,可以处理静态文件。

如果你想知道 servlet 如何处理的,可以参考我的另一个项目:

手写从零实现简易版 tomcat minicat

手写 nginx 系列

如果你对 nginx 原理感兴趣,可以阅读:

从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?

从零手写实现 nginx-02-nginx 的核心能力

从零手写实现 nginx-03-nginx 基于 Netty 实现

从零手写实现 nginx-04-基于 netty http 出入参优化处理

从零手写实现 nginx-05-MIME类型(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)

从零手写实现 nginx-06-文件夹自动索引

从零手写实现 nginx-07-大文件下载

从零手写实现 nginx-08-范围查询

从零手写实现 nginx-09-文件压缩

从零手写实现 nginx-10-sendfile 零拷贝

从零手写实现 nginx-11-file+range 合并

从零手写实现 nginx-12-keep-alive 连接复用

从零手写实现 nginx-13-nginx.conf 配置文件介绍

从零手写实现 nginx-14-nginx.conf 和 hocon 格式有关系吗?

从零手写实现 nginx-15-nginx.conf 如何通过 java 解析处理?

从零手写实现 nginx-16-nginx 支持配置多个 server

基本实现

思路

基于 serverSocket 实现最基本的监听。

核心

java 复制代码
    public void start() {
        try {
            // 服务器监听的端口号
            int port = nginxConfig.getHttpServerListen();
            ServerSocket serverSocket = new ServerSocket(port);
            log.info("[Nginx4j] listen on port={}", port);

            while (true) {
                Socket socket = serverSocket.accept();
                log.info("[Nginx4j] Accepted connection from address={}", socket.getRemoteSocketAddress());
                handleClient(socket);
            }
        } catch (Exception e) {
            log.info("[Nginx4j] meet ex", e);

            throw new RuntimeException(e);
        }
    }

socket 的处理

java 复制代码
private void handleClient(Socket socket) {
        try {
            // 基本信息
            InputStream input = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            String header = reader.readLine();
            String[] parts = header.split(" ");
            String method = parts[0];
            String path = parts[1];
            String protocol = parts[2];

            // 根路径
            final String basicPath = nginxConfig.getHttpServerRoot();
            // 只处理GET请求
            if ("GET".equalsIgnoreCase(method)) {
                //root path
                if(StringUtil.isEmpty(path) || "/".equals(path)) {
                    log.info("[Nginx4j] current path={}, match index path", path);
                    byte[] fileContent = tryGetIndexContent();
                    sendResponse(socket, 200, "OK", fileContent);
                    return;
                }

                // other
                File file = new File(basicPath + path);
                if (file.exists()) {
                    byte[] fileContent = Files.readAllBytes(file.toPath());
                    sendResponse(socket, 200, "OK", fileContent);
                } else {
                    sendResponse(socket, 404, "Not Found", "File not found.".getBytes());
                }
            } else {
                sendResponse(socket, 405, "Method Not Allowed", "Method not allowed.".getBytes());
            }
        } catch (Exception e) {
            // 异常处理...
        }
    }

响应的处理

java 复制代码
private void sendResponse(Socket socket, int statusCode, String statusMessage, byte[] content) throws IOException {
        OutputStream output = socket.getOutputStream();
        PrintWriter writer = new PrintWriter(output, true);

        // 发送HTTP响应头
        writer.println("HTTP/1.1 " + statusCode + " " + statusMessage);
        writer.println("Content-Type: text/plain");
        writer.println("Content-Length: " + content.length);
        writer.println("Connection: close");
        writer.println();

        // 发送HTTP响应体
        output.write(content);
        output.flush();
    }

测试

快速开始

maven 依赖

xml 复制代码
<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>nginx4j</artifactId>
    <version>0.1.0</version>
</dependency>

启动测试

java 复制代码
Nginx4jBs.newInstance().init().start();

启动日志:

ini 复制代码
[DEBUG] [2024-05-24 23:40:37.573] [main] [c.g.h.l.i.c.LogFactory.setImplementation] - Logging initialized using 'class com.github.houbb.log.integration.adaptors.stdout.StdOutExImpl' adapter.
[INFO] [2024-05-24 23:40:37.576] [main] [c.g.h.n.s.s.NginxServerSocket.start] - [Nginx4j] listen on port=8080

页面访问:http://127.0.0.1:8080

响应:

css 复制代码
Welcome to nginx4j!

其他页面

访问 http://localhost:8080/1.txt

将返回对应的文件内容:

复制代码
hello nginx4j!

开源地址

为了便于大家学习,已经将 nginx 开源

github.com/houbb/nginx...

相关推荐
思想在飞肢体在追6 分钟前
Springboot项目配置Nacos
java·spring boot·后端·nacos
cyforkk8 分钟前
09、Java 基础硬核复习:异常处理(容错机制)的核心逻辑与面试考点
java·数据库·面试
??(lxy)29 分钟前
java高性能无锁队列——MpscLinkedQueue
java·开发语言
数研小生33 分钟前
Full Analysis of Taobao Item Detail API taobao.item.get
java·服务器·前端
Wang15301 小时前
Java编程基础与面向对象核心概念
java
毕设源码-郭学长1 小时前
【开题答辩全过程】以 康复管理系统为例,包含答辩的问题和答案
java
毅炼1 小时前
hot100打卡——day17
java·数据结构·算法·leetcode·深度优先
winfreedoms1 小时前
java-网络编程——黑马程序员学习笔记
java·网络·学习
开开心心就好1 小时前
键盘改键工具免安装,自定义键位屏蔽误触
java·网络·windows·随机森林·计算机外设·电脑·excel
IManiy1 小时前
总结之Temporal全局速率控制(二)第三方速率控制服务设计
java