8.3 Java HTTP

HTTP协议 参考 1.2http协议-CSDN博客文章浏览阅读1.8k次,点赞68次,收藏23次。HTTP 用socket发送http响应给浏览器 https://chxii.blog.csdn.net/article/details/146284464?spm=1011.2415.3001.5331

 java 使用ServerSocket 根据 浏览器 请求,做出响应

基于 Java 的简单 HTTP 服务器实现,响应 GET 请求,返回固定简单html,并支持静态资源(如 favicon.ico)的返回。


关键技术点

  • HTTP 协议解析

    • 手动读取并解析 HTTP 请求行和请求头。
    • 支持基础的 URL 路径判断(如 /favicon.ico)。
  • 字符编码处理

    • 使用 StandardCharsets.UTF_8 统一处理输入输出编码,避免乱码问题。
  • 时区与时间格式化

    • 使用 ZonedDateTime 和 ZoneId.of("UTC") 构造符合 RFC 1123 标准的日期头字段。
  • 静态资源服务

    • 提供对 favicon.ico 的特殊处理,支持二进制文件发送。
  • 异常处理机制

    • 捕获并打印服务器启动和请求处理中的异常信息。
    • 返回对应的错误页面(如 405、500)。
启动方式:

java Test1

默认监听端口:8080
默认网站根目录:www/
需确保 www/favicon.ico 存在,否则将返回空响应。

测试访问:
  • 访问 http://localhost:8080 将显示欢迎语句。

该项目用途:

  • 学习 HTTP 协议底层原理

项目代码:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Test1 {
    private static final int PORT = 8080;
    private static final String ROOT_DIR = "www";
    private static final String FAVICON_PATH = ROOT_DIR + "/favicon.ico";

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("服务器启动,监听端口: " + PORT);
            
            while (true) {
                try (Socket clientSocket = serverSocket.accept()) {
                    System.out.println("客户端连接: " + clientSocket.getInetAddress());
                    
                    BufferedReader in = new BufferedReader(
                        new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8));
                    PrintWriter out = new PrintWriter(
                        new OutputStreamWriter(clientSocket.getOutputStream(), StandardCharsets.UTF_8), true);
                    
                    String requestLine = in.readLine();
                    if (requestLine == null) continue;
                    
                    System.out.println("请求行: " + requestLine);
                    
                    String[] requestParts = requestLine.split(" ");
                    if (requestParts.length < 2) continue;
                    
                    String method = requestParts[0];
                    String path = requestParts[1];
                    
                    // 读取请求头(忽略)
                    String headerLine;
                    while ((headerLine = in.readLine()) != null && !headerLine.isEmpty()) {
                        System.out.println("请求头: " + headerLine);
                    }
                    
                    if ("GET".equalsIgnoreCase(method)) {
                        //handleGetRequest(path, out, clientSocket.getOutputStream());
                         // 特别处理favicon.ico
                        if ("/favicon.ico".equals(path)) {
                            handleFaviconRequest(out,  clientSocket.getOutputStream());
                            return;
                        }
                        sendResponse(out, 200, "text/html", "<h1>欢迎使用虚拟线程Web服务器</h1>");
                    } else {
                        sendResponse(out, 405, "Method Not Allowed", "该方法不被支持");
                    }
                } catch (IOException e) {
                    System.err.println("处理请求时出错: " + e.getMessage());
                }
            }
        } catch (IOException e) {
            System.err.println("服务器启动失败: " + e.getMessage());
        }
    }

    

    private static void handleFaviconRequest(PrintWriter out, OutputStream rawOut) {
        File favicon = new File(FAVICON_PATH);
        
        if (favicon.exists() && favicon.isFile()) {
            try {
           
                // 使用ZonedDateTime并指定时区为UTC
                String date = ZonedDateTime.now(ZoneId.of("UTC"))
                     .format(DateTimeFormatter.RFC_1123_DATE_TIME);
                
                // 发送响应头
                StringBuilder responseHeader = new StringBuilder();
                responseHeader.append("HTTP/1.1 200 OK\r\n");
                responseHeader.append("Date: ").append(date).append("\r\n");
                responseHeader.append("Server: SimpleTCPServer/1.0\r\n");
                responseHeader.append("Content-Type: image/x-icon\r\n");
                responseHeader.append("Content-Length: ").append(favicon.length()).append("\r\n");
                responseHeader.append("Connection: close\r\n");
                responseHeader.append("\r\n");
                
                // 发送响应头
                out.print(responseHeader.toString());
                out.flush();
                
                // 发送图标二进制数据
                try (FileInputStream fis = new FileInputStream(favicon)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        rawOut.write(buffer, 0, bytesRead);
                    }
                }
                rawOut.flush();
            } catch (IOException e) {
                sendResponse(out, 500, "Internal Server Error", "服务器内部错误");
            }
        } else {
            // 如果没有提供图标,返回一个空的图标响应
            sendEmptyFaviconResponse(out);
        }
    }

    private static void sendEmptyFaviconResponse(PrintWriter out) {
        // 使用ZonedDateTime并指定时区为UTC
        String date = ZonedDateTime.now(ZoneId.of("UTC"))
        .format(DateTimeFormatter.RFC_1123_DATE_TIME);
        String response = """
            HTTP/1.1 200 OK
            Date: %s
            Server: SimpleTCPServer/1.0
            Content-Type: image/x-icon
            Content-Length: 0
            Connection: close
            
            """.formatted(date);
        out.print(response);
        out.flush();
    }

    private static void sendResponse(PrintWriter out, int statusCode, String contentType, String content) {
        String statusLine = "HTTP/1.1 " + statusCode + " " + getStatusMessage(statusCode);
        // 使用ZonedDateTime并指定时区为UTC
        String date = ZonedDateTime.now(ZoneId.of("UTC"))
             .format(DateTimeFormatter.RFC_1123_DATE_TIME);
        
        StringBuilder response = new StringBuilder();
        response.append(statusLine).append("\r\n");
        response.append("Date: ").append(date).append("\r\n");
        response.append("Server: SimpleTCPServer/1.0\r\n");
        response.append("Content-Type: ").append(contentType).append("; charset=utf-8\r\n");
        response.append("Content-Length: ").append(content.getBytes().length).append("\r\n");
        response.append("Connection: close\r\n");
        response.append("\r\n");
        response.append(content);
        
        out.print(response.toString());
        out.flush();
    }

    

    private static String getStatusMessage(int statusCode) {
        return switch (statusCode) {
            case 200 -> "OK";
            case 404 -> "Not Found";
            case 405 -> "Method Not Allowed";
            case 500 -> "Internal Server Error";
            default -> "Unknown Status";
        };
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chxii

小小打赏,大大鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值