从零手写实现 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...

相关推荐
终将老去的穷苦程序员2 分钟前
基于Android Studio开发的安卓图书借阅管理系统
java·sqlite·android studio·android-studio
技术小结-李爽40 分钟前
【工具】Maven的使用
java·maven
sou_time42 分钟前
从 0 到 商用:AI Agent x SKILL x MCP 全栈实战教程:L2 高等篇:MCP 协议 + Spring AI + Agent 编排
java·人工智能·spring
冷小鱼1 小时前
高级研发编码习惯:从规范到艺术,再到AI+时代的人机协同
java·开发语言·python·编码习惯
齐 飞1 小时前
JDK21虚拟线程
java·后端
小马爱打代码1 小时前
Java 并发 Bug 深度分析与实战
java
极客先躯1 小时前
高级java每日一道面试题-2026年02月09日-实战篇[Docker]-Docker 容器有哪些安全风险?如何缓解?
java·运维·网络·安全·docker·容器
_Aaron___1 小时前
MyBatis 动态排序别乱用 ${}:ORDER BY 的安全写法
java·spring·mybatis
摇滚侠1 小时前
SpringMVC 入门到实战 HttpMessageConverter 65-74
java·后端·spring·intellij-idea
逢君学术论文AI写作1 小时前
Java第24课:会话技术CookieSession
java·开发语言