You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java简易Web服务器OutputStream写入失败及连接问题求助

你的Web服务器代码问题分析与修复

核心错误点

  • 未定义变量thefile:你只定义了字符串类型的file,但后续使用的thefile是File对象,完全没声明,直接编译失败,服务器根本启动不起来,这是浏览器连不上的主要原因。
  • 代码块闭合错误:处理完请求解析后多了一个},导致后续写入响应的代码不在try块内,编译报错。
  • 响应输出到控制台而非浏览器:PrintWriter response = new PrintWriter(System.out);把HTTP响应打印到了服务器控制台,没有发送给客户端的Socket输出流,浏览器自然收不到内容。
  • 请求读取逻辑存在空指针风险:br.readLine()在流结束时会返回null,原代码的while (!line.isEmpty())会触发NullPointerException,导致连接异常中断。
  • HTTP响应格式错误:响应头结束后缺少一个空行(\r\n\r\n),浏览器无法区分响应头和响应体,即使收到数据也无法解析。
  • 未处理文件不存在的场景:文件不存在时仅在控制台打印信息,没有给浏览器返回404错误响应,导致浏览器一直等待连接超时。
  • 根路径请求未处理:当请求localhost:50505时,file会是/,需要默认映射到某个文件(比如index.html),否则会尝试访问根目录,无法读取。

修正后的完整代码

package lab11;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.OutputStream;
import java.io.PrintWriter;

public class Main { // 修正类名,符合Java规范
    private static final int LISTENING_PORT = 50505;
    // 建议使用相对路径,确保files目录在项目根目录下
    private static final String ROOT_DIRECTORY = "./files";

    public static void main(String[] args) {
        ServerSocket serverSocket;
        try {
            serverSocket = new ServerSocket(LISTENING_PORT);
        } catch (Exception e) {
            System.out.println("Failed to create listening socket.");
            e.printStackTrace();
            return;
        }
        System.out.println("Listening on port " + LISTENING_PORT);
        try {
            while (true) {
                Socket connection = serverSocket.accept();
                System.out.println("\nConnection from " + connection.getRemoteSocketAddress());
                handleConnection(connection);
            }
        } catch (Exception e) {
            System.out.println("Server socket shut down unexpectedly!");
            System.out.println("Error: " + e);
            System.out.println("Exiting.");
        }
    }

    public static void handleConnection(Socket sok) {
        try {
            InputStream one = sok.getInputStream();
            InputStreamReader isr = new InputStreamReader(one);
            BufferedReader br = new BufferedReader(isr);

            StringBuilder request = new StringBuilder();
            String line;
            // 修正读取逻辑,避免空指针
            line = br.readLine();
            while (line != null && !line.isEmpty()) {
                request.append(line + "\r\n");
                line = br.readLine();
            }

            String[] splitline = request.toString().split("\r\n");
            String getLine = null;
            String filePath = "/"; // 默认请求根路径
            for (String i : splitline) {
                if (i.startsWith("GET")) { // 更精准匹配GET请求行
                    getLine = i;
                    String[] splitget = getLine.split(" ");
                    if (splitget.length >= 2) {
                        filePath = splitget[1];
                    }
                    break; // 只处理第一行GET请求
                }
            }

            // 处理根路径请求,默认返回index.html
            if (filePath.equals("/")) {
                filePath = "/index.html";
            }

            // 构建File对象,修正变量未定义问题
            File theFile = new File(ROOT_DIRECTORY + filePath);
            // 安全校验:防止路径遍历攻击,确保文件在根目录内
            if (!theFile.getCanonicalPath().startsWith(new File(ROOT_DIRECTORY).getCanonicalPath())) {
                sendErrorResponse(sok.getOutputStream(), 403, "Forbidden");
                return;
            }

            OutputStream socketOut = sok.getOutputStream();
            if (theFile.exists() && theFile.isFile() && theFile.canRead()) {
                // 发送200响应头,输出流改为Socket的OutputStream
                PrintWriter responseWriter = new PrintWriter(socketOut);
                responseWriter.write("HTTP/1.1 200 OK\r\n");
                responseWriter.write("Connection: close\r\n");
                responseWriter.write("Content-Length: " + theFile.length() + "\r\n");
                responseWriter.write("\r\n"); // 响应头结束的空行,必须添加
                responseWriter.flush();

                sendFile(theFile, socketOut);
            } else {
                // 给浏览器返回404错误响应
                sendErrorResponse(socketOut, 404, "Not Found");
                System.out.println("File not found: " + theFile.getAbsolutePath());
            }

        } catch (Exception e) {
            System.out.println("Error while communicating with client: " + e);
            e.printStackTrace();
            try {
                // 发生异常时返回500错误
                sendErrorResponse(sok.getOutputStream(), 500, "Internal Server Error");
            } catch (IOException ex) {
                // 忽略发送错误的异常
            }
        } finally {
            try {
                sok.close();
            } catch (Exception e) {
            }
            System.out.println("Connection closed.");
        }
    }

    private static void sendErrorResponse(OutputStream socketOut, int statusCode, String statusMsg) throws IOException {
        PrintWriter writer = new PrintWriter(socketOut);
        writer.write(String.format("HTTP/1.1 %d %s\r\n", statusCode, statusMsg));
        writer.write("Connection: close\r\n");
        writer.write("Content-Type: text/plain\r\n");
        writer.write("Content-Length: " + statusMsg.length() + "\r\n");
        writer.write("\r\n");
        writer.write(statusMsg);
        writer.flush();
    }

    private static void sendFile(File file, OutputStream socketOut) throws IOException {
        InputStream in = new BufferedInputStream(new FileInputStream(file));
        OutputStream out = new BufferedOutputStream(socketOut);
        byte[] buffer = new byte[4096]; // 使用字节数组提高传输效率,替代单字节读取
        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
        out.flush();
        in.close();
    }
}

使用说明

  1. 在项目根目录下创建files文件夹,放入测试文件(比如index.html)。
  2. 启动服务器后,访问http://localhost:50505会返回files/index.html,访问http://localhost:50505/test.txt会返回files/test.txt。
  3. 如果文件不存在,浏览器会收到404错误提示。

内容的提问来源于stack exchange,提问作者Hoko L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 09:55:15