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

Java本地HTTP服务器调用JOptionPane报错及加载异常求助

Java嵌入式Web服务器:NullPointerException与JOptionPane调用问题

问题描述

我正在开发一个Java项目,需要在客户端机器启动WEB服务器,实现浏览器加载页面的同时调用JOptionPane,但运行时出现了NullPointerException,错误信息如下:

Exception in thread "Thread-5" java.lang.NullPointerException 
at java.util.StringTokenizer.<init>(StringTokenizer.java:199) 
at java.util.StringTokenizer.<init>(StringTokenizer.java:236) 
at pt.paysafe.Server.run(Server.java:44) 
at java.lang.Thread.run(Thread.java:748)

我尝试移除PaySafe.java中的线程,直接调用server.run(),但页面会无限加载且无报错信息,希望能得到解决方案。

问题分析与解决方案

1. 为什么会出现NullPointerException?

这个错误出现在StringTokenizer parse = new StringTokenizer(input);这一行,原因是in.readLine()可能返回null——当客户端连接异常关闭,或者没有发送合法的HTTP请求头时,input变量就会是null,直接传给StringTokenizer构造器就会抛出NPE。

解决方法:在处理input之前先做非空检查,如果input为null,直接关闭连接即可:

String input = in.readLine();
if (input == null || input.isEmpty()) {
    // 非法请求,直接关闭连接
    return;
}

2. 直接调用server.run()导致页面无限加载的原因

当你去掉线程直接调用server.run()时,servidorHttp()里的while(true)循环会同步执行每个请求的处理逻辑,而JOptionPane.showMessageDialog()是模态对话框,它会阻塞当前线程直到用户关闭对话框——这就导致HTTP响应无法及时发送给浏览器,浏览器一直在等待响应,所以页面无限加载。

另外还有一个关键问题:Swing组件必须在事件调度线程(EDT)中创建和显示,你在Web服务器的工作线程中直接调用JOptionPane,会触发线程安全问题,可能导致界面异常或卡住。

解决方法:

  • 保留线程处理,确保Web服务器可以同时处理多个请求,不会被阻塞
  • 使用SwingUtilities.invokeLater()将JOptionPane的调用切换到EDT执行

修改后的完整代码

PaySafe.java(无需修改,保留线程逻辑)

package pt.paysafe;
import java.io.IOException;
import java.net.ServerSocket;
import javax.swing.JOptionPane;

public class PaySafe {
    static final int PORT = 8080;
    private static void servidorHttp(){
        try{
            ServerSocket serverConnect = new ServerSocket(PORT);
            while(true){
                Servidor server = new Servidor(serverConnect.accept());
                Thread thread = new Thread(server);
                thread.start();
            }
        } catch (IOException ex){
            JOptionPane.showMessageDialog(null, ex.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
        }
    }
    public static void main(String[] args){
        servidorHttp();
    }
}

Servidor.java(关键修改点标注)

package pt.paysafe;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import java.util.StringTokenizer;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Servidor implements Runnable{
    static final File WebRoot = new File(".");
    static final String DefaultPage = "index.html";
    static final String FileNotFound = "404.html";
    static final String MethodNotSupported = "notSupported.html";
    private Socket connect;

    public Servidor(Socket c) {
        connect = c;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        BufferedOutputStream dataOut = null;
        String fileRequested = null;

        try{
            in = new BufferedReader(new InputStreamReader(connect.getInputStream()));
            out = new PrintWriter(connect.getOutputStream());
            dataOut = new BufferedOutputStream(connect.getOutputStream());

            String input = in.readLine();
            // 新增:检查input是否为null,避免NPE
            if (input == null || input.trim().isEmpty()) {
                return;
            }
            
            StringTokenizer parse = new StringTokenizer(input);
            String method = parse.nextToken().toUpperCase();
            fileRequested = parse.nextToken().toLowerCase();

            if(!method.equals("GET") && !method.equals("HEAD")){
                File file = new File(WebRoot, MethodNotSupported);
                int fileLength = (int) file.length();
                String contentMimeType = "text/html";
                byte[] fileData = readFileData(file, fileLength);

                out.println("HTTP/1.1 501 Not Implemented");
                out.println("Server: Servidor Plugin PaySafe");
                out.println("Date: " + new Date());
                out.println("Content-type: " + contentMimeType);
                out.println("Content-length: " + fileLength);
                out.println();
                out.flush();
                dataOut.write(fileData, 0, fileLength);
                dataOut.flush();
            }else{
                if(fileRequested.endsWith("/")){
                    fileRequested += DefaultPage;
                }
                File file = new File(WebRoot, fileRequested);
                int fileLength = (int) file.length();
                String contentmt = getContentType(fileRequested);

                if(method.equals("GET")){
                    // 新增:使用SwingUtilities.invokeLater在EDT显示对话框
                    SwingUtilities.invokeLater(() -> {
                        JOptionPane.showMessageDialog(null, "Sucesso!", "OK", JOptionPane.INFORMATION_MESSAGE);
                    });
                    
                    byte[] fileData = readFileData(file, fileLength);

                    out.println("HTTP/1.1 200 OK");
                    out.println("Server: Servidor Plugin PaySafe");
                    out.println("Date: " + new Date());
                    out.println("Content-type: " + contentmt);
                    out.println("Content-length: " + fileLength);
                    out.println();
                    out.flush();
                    dataOut.write(fileData, 0, fileLength);
                    dataOut.flush();
                }
            }
        }catch (FileNotFoundException fnfe){
            try{
                fileNotFound(out, dataOut, fileRequested);
            }catch(IOException ioe){
                JOptionPane.showMessageDialog(null, ioe.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
            }
        } catch (IOException ex) {
            // 注意:这里如果是工作线程,同样需要切换到EDT显示对话框
            SwingUtilities.invokeLater(() -> {
                JOptionPane.showMessageDialog(null, ex.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
            });
        } finally {
            try {
                if(in != null) in.close();
                if(out != null) out.close();
                if(dataOut != null) dataOut.close();
                if(connect != null) connect.close();
            } catch (Exception e) {
                SwingUtilities.invokeLater(() -> {
                    JOptionPane.showMessageDialog(null, e.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
                });
            }
        }
    }

    private byte[] readFileData(File file, int fileLength) throws IOException {
        FileInputStream fileIn = null;
        byte[] fileData = new byte[fileLength];
        try{
            fileIn = new FileInputStream(file);
            fileIn.read(fileData);
        }finally{
            if(fileIn != null){
                fileIn.close();
            }
        }
        return fileData;
    }

    private String getContentType(String fileRequested) {
        if(fileRequested.endsWith(".htm") || fileRequested.endsWith(".html")){
            return "text/html";
        }else if(fileRequested.endsWith(".ico")){
            return "image/x-icon"; // 修正:标准MIME类型是image/x-icon,不是image/ico
        }else if(fileRequested.endsWith(".png")){
            return "image/png";
        }else{
            return "text/plain";
        }
    }

    private void fileNotFound(PrintWriter out, OutputStream dataOut, String fileRequested) throws IOException {
        File file = new File(WebRoot, FileNotFound);
        int fileLenght = (int) file.length();
        String content = "text/html";
        byte[] fileData = readFileData(file, fileLenght);

        out.println("HTTP/1.1 404 Not Found");
        out.println("Server: Servidor Plugin PaySafe");
        out.println("Date: " + new Date());
        out.println("Content-type: " + content);
        out.println("Content-length: " + fileLenght);
        out.println();
        out.flush();
        dataOut.write(fileData, 0, fileLenght);
        dataOut.flush();
    }
}

额外注意事项

  • 我还修正了getContentType方法中.ico文件的MIME类型,标准类型是image/x-icon,避免浏览器解析图标时出现问题
  • 所有JOptionPane的调用都切换到了EDT,确保Swing界面操作的线程安全性
  • 保留线程池(或者至少每个请求一个线程)是Web服务器的基本要求,否则无法处理并发请求,也会被模态对话框阻塞

内容的提问</think_never_used_51bce0c785ca2f68081bfa7d91973934>

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:26:24