ServerSocket关闭方法不释放端口

时间:2018-01-22 13:08:28

标签: java sockets server port release

我正在开发2个应用程序(Web和独立应用程序)。我已在Web应用程序中构建了功能,以便用户能够从网页重新启动独立应用程序的某些模块。我实现这一点的方法是使用一个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个应用程序后,用户可能需要更改此侦听器正在运行的端口。当他们确实从Web应用程序更改它时,我提取未更改的端口,然后在数据库中更新它并将其发送到以下方法:

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状态。我可以连续几次更改它并且每次都可以工作,但端口不会被释放。我花了很长时间研究这个问题,从我所读到的内容看来,我正在做的一切都是正确的(显然不是!)。

无论你们有什么输入都会有所帮助。

干杯!

2 个答案:

答案 0 :(得分:0)

我建议你使用资源尝试块,或者至少使用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());
    }

答案 1 :(得分:0)

我将我的WebClientConnector类的run方法(它实现了Runnable)更改为以下代码:

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

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