ServerSocket关闭方法不释放端口

ServerSocket close method not releasing port

我正在开发 2 个应用程序(网络应用程序和独立应用程序)。我在网络应用程序中内置了功能,以便用户能够从网页重新启动独立应用程序的某些模块。我完成此操作的方法是使用一个 ServerSocket 对象,该对象侦听在数据库中配置为参数的端口。这是监听传入请求的服务器端的简化版本:

try
    {
        int port = Integer.parseInt(globalParamService.findByName("serviceInterconnectPort").getValue());
        ServerSocket serverSocket = new ServerSocket(port);
        logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully started web client connector on port " + port);
        while(running)
        {
            socket = serverSocket.accept();
            logEntryService.logInfo(LogEntry.CONNECTIVITY, "Incoming request from web client");
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            if (message.contains("Restart Web Client Connector"))
            {
                if (!main.isWebClientConnectorRestarting())
                {
                    main.restartWebClientConnector();
                    String returnMessage = "Done\n";
                    OutputStream os = socket.getOutputStream();
                    OutputStreamWriter osw = new OutputStreamWriter(os);
                    BufferedWriter bw = new BufferedWriter(osw);
                    bw.write(returnMessage);
                    bw.flush();

                    os.close();
                    osw.close();
                    bw.close();
                }
                else
                {
                    String returnMessage = "Request cancelled\n";
                    OutputStream os = socket.getOutputStream();
                    OutputStreamWriter osw = new OutputStreamWriter(os);
                    BufferedWriter bw = new BufferedWriter(osw);
                    bw.write(returnMessage);
                    bw.flush();

                    os.close();
                    osw.close();
                    bw.close();
                    logEntryService.logWarning(LogEntry.CONNECTIVITY, "Web client connector restart request cancelled, restart already in progress");
                }
            }

            is.close();
            isr.close();
            br.close();
            socket.close();
        }
    }
    catch (Exception ex)
    {
        logEntryService.logError(LogEntry.CONNECTIVITY, "Error processing restart request from web client : " + ex.getMessage());
    }

部署我的 2 个应用程序后,用户可能需要更改此侦听器所在的端口 运行。当他们确实从网络应用程序更改它时,我在数据库中更新之前提取未更改的端口并将其发送到以下方法:

public void restartWebClientConnector(int oldPort)
{
    Thread t = new Thread(() -> 
    {
        try
        {
            logEntryService.logInfo(LogEntry.CONNECTIVITY, "Connecting to port " + oldPort + " to restart web client connector");
            InetAddress address = InetAddress.getByName("localhost");
            socket = new Socket(address, oldPort);
            logEntryService.logDebug(LogEntry.CONNECTIVITY, "Successfully connected to port " + oldPort);
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os);
            BufferedWriter bw = new BufferedWriter(osw);
            bw.write("Restart Web Client Connector\n");
            bw.flush();
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            if (message.compareTo("Done") == 0)
            {
                logEntryService.logInfo(LogEntry.CONNECTIVITY, "Web client connector restart request acknowledged");
            }
            else
            {
                logEntryService.logWarning(LogEntry.CONNECTIVITY, "Web client connector restart request cancelled, restart already in progress");
            }

            os.close();
            osw.close();
            bw.close();
            is.close();
            isr.close();
            br.close();
            socket.close();
        }
        catch (IOException | NumberFormatException ex)
        {
            logEntryService.logError(LogEntry.CONNECTIVITY, "Error sending web client connector restart command : " + ex.getMessage());
        }
    });
    t.start();
}

此方法然后调用以下代码终止我的侦听器线程并在新更新的端口号上重新初始化它:

public void restartWebClientConnector()
{
    if (!webClientConnectorRestarting)
    {
        webClientConnectorRestarting = true;
        webClientConnector.setRunning(false);
        webClientConnectorThread.interrupt();
        initWebClientConnector();
        logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully restarted web client connector");
        webClientConnectorRestarting = false;
    }
}

private void initWebClientConnector()
{
    logEntryService.logInfo(LogEntry.CORE, "Initializing web connector"); 

    try
    {
        webClientConnector = new WebClientConnector(this, globalParamService, logEntryService);
        webClientConnectorThread = new Thread(threads, webClientConnector);
        webClientConnectorThread.setName("Web Client Connector Thread");
        webClientConnectorThread.start();
    }
    catch (Exception ex)
    {
        logEntryService.logError(LogEntry.CORE, "Error initializing messaging process : " + ex.getMessage()); 
    }
}

一切都很好,除了一件事:尽管明确关闭了我能想到的一切,但旧端口号并未释放。在从 Web 客户端更改端口后执行 netstat -a 时,旧端口仍列在 LISTENING 状态。我可以连续多次更改它并且每次都有效,但端口没有被释放。我花了很多时间研究这个,从我读到的内容来看,我似乎做的一切都是正确的(显然不是!)。

无论你们有什么意见都会有所帮助。

干杯!

我建议您使用资源 try 块,或者至少使用 try-finally 块来确保资源已关闭:

    int port = Integer.parseInt(globalParamService.findByName("serviceInterconnectPort").getValue());
    try (ServerSocket serverSocket = new ServerSocket(port)) {
        logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully started web client connector on port " + port);
        while (running) {
            try (Socket socket = serverSocket.accept();
                    InputStream is = socket.getInputStream();
                    InputStreamReader isr = new InputStreamReader(is);
                    BufferedReader br = new BufferedReader(isr)) {
                String message = br.readLine();
                if (message.contains("Restart Web Client Connector")) {
                    if (!main.isWebClientConnectorRestarting()) {
                        main.restartWebClientConnector();
                        String returnMessage = "Done\n";
                        try (OutputStream os = socket.getOutputStream();
                                OutputStreamWriter osw = new OutputStreamWriter(os);
                                BufferedWriter bw = new BufferedWriter(osw)) {
                            bw.write(returnMessage);
                            bw.flush();
                        }
                    } else {
                        String returnMessage = "Request cancelled\n";
                        try (OutputStream os = socket.getOutputStream();
                                OutputStreamWriter osw = new OutputStreamWriter(os);
                                BufferedWriter bw = new BufferedWriter(osw)) {
                            bw.write(returnMessage);
                            bw.flush();
                        }
                        logEntryService.logWarning(LogEntry.CONNECTIVITY, "Web client connector restart request cancelled, restart already in progress");
                    }
                }
            }
        }
    } catch (IOException ex) {
        logEntryService.logError(LogEntry.CONNECTIVITY, "Error processing restart request from web client : " + ex.getMessage());
    }

我将我的 WebClientConnector class' 运行 方法(它实现了 Runnable)更改为以下代码:

@Override
public void run() 
{
    try
    {
        initWebClientListener();
    }
    finally
    {
        try 
        {
            serverSocket.close();
        } 
        catch (IOException ex1) 
        {}
    }
}

并更改了我的重启方法以对其调用中断并将 运行ning 变量设置为 false。 ServerSocket 现在正确关闭并且端口被释放。