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

JavaFX邮件服务器重启报Address already in use问题求助

问题描述

我正在使用JavaFX结合MVC架构开发邮件应用,实现了服务器的run()、stopServer()和startServer()方法,但重启服务器时抛出java.net.BindException: Address already in use (Bind failed)错误,已定位问题为端口未释放,求修复方案。

服务器核心方法实现

run() 方法

@Override
public void run() {
    try {
        serverSocket = new ServerSocket(PORT);
        controller.logEvent(Protocol.SERVER_STARTED, true, null, null);
        loadAccounts();
        while (isRunning.get()) {
            try {
                Socket socket = serverSocket.accept();
                if (isRunning.get()) {
                    connectedClients.add(socket);
                    Thread thread = new Thread(() -> {
                        handleClient(socket);
                        connectedClients.remove(socket);
                    });
                    thread.start();
                } else {
                    socket.close();
                }
            } catch (SocketException e) {
                if (!isRunning.get()) {
                    break;
                } else {
                    throw e;
                }
            }
        }
    } catch (IOException e) {
        controller.logEvent(Protocol.SERVER_STARTED, false, null, null);
        e.printStackTrace();
    }
}

stopServer() 方法

public void stopServer() {
    isRunning.set(false);
    try {
        if (serverSocket != null) {
            serverSocket.setReuseAddress(true);
            serverSocket.close();
            controller.logEvent(Protocol.SERVER_STOPPED, true, null, null);
            for (Socket socket : connectedClients) {
                socket.close();
            }
            connectedClients.clear();
        }
    } catch (IOException e) {
        controller.logEvent(Protocol.SERVER_STOPPED, false, null, null);
        e.printStackTrace();
    }
}

startServer() 方法

public void startServer() {
    try {
        if (serverSocket != null && !serverSocket.isClosed()) {
            serverSocket.setReuseAddress(true);
            serverSocket.close();
        }
        isRunning.set(true);
        serverSocket = new ServerSocket(PORT);
        Thread thread = new Thread(this);
        thread.start();
        controller.logEvent(Protocol.SERVER_STARTED, true, null, null);
    } catch (IOException e) {
        controller.logEvent(Protocol.SERVER_STARTED, false, null, null);
    }
}

错误日志

java.net.BindException: Address already in use (Bind failed)
    at java.base/java.net.PlainSocketImpl.socketBind(Native Method)
    at java.base/java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:452)
    at java.base/java.net.ServerSocket.bind(ServerSocket.java:395)
    at java.base/java.net.ServerSocket.<init>(ServerSocket.java:257)
    at java.base/java.net.ServerSocket.<init>(ServerSocket.java:149)
    at MailServer.run(MailServer.java:71)
    at java.base/java.lang.Thread.run(Thread.java:829)
解决方案

你的代码存在多处导致端口未释放的问题,逐一修复如下:

  1. 修正SO_REUSEADDR设置时机
    serverSocket.setReuseAddress(true)必须在绑定端口前设置,当前在关闭时设置完全无效。修改run()方法中的ServerSocket初始化逻辑:
// 替换run()里的serverSocket初始化代码
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(PORT));
  1. 消除重复绑定端口
    你在startServer()和run()中都创建了ServerSocket(PORT),导致两次绑定同一端口。二选一保留一处即可:
  • 推荐保留run()中的初始化,删除startServer()里的serverSocket = new ServerSocket(PORT)代码。
  1. 确保客户端连接彻底清理
    遍历关闭客户端socket时,单个socket关闭异常会中断整个清理流程,需给每个socket的关闭加try-catch:
for (Socket socket : connectedClients) {
    try {
        if (!socket.isClosed()) {
            socket.close();
        }
    } catch (IOException e) {
        e.printStackTrace(); // 或记录日志,不中断后续清理
    }
}
  1. 解决并发安全问题
    connectedClients如果是普通ArrayList,多线程增删会出问题,改用线程安全的集合:
private List<Socket> connectedClients = new CopyOnWriteArrayList<>();
  1. 优化服务器线程停止逻辑
    调用stopServer()后,serverSocket.accept()会阻塞线程,可通过中断线程加快退出:
// 在MailServer类中添加线程引用
private Thread serverThread;

// startServer()中保存线程
serverThread = new Thread(this);
serverThread.start();

// stopServer()中添加中断逻辑
if (serverThread != null && serverThread.isAlive()) {
    serverThread.interrupt();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:33:30