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>
相关产品推荐
相关产品推荐

