带套接字的Java单实例软件.在Windows下关闭套接字的问题 [英] Java single instance software with socket. issue in closing socket under windows

查看:111
本文介绍了带套接字的Java单实例软件.在Windows下关闭套接字的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要强制Java应用程序在单个实例上运行.我在此链接上找到了这段非常好的代码,使用套接字而不是文件系统来解决问题.

I need to force my Java application to run with a single instance. I found on this link this very nice piece of code that solve the problem using socket instead of using the file system.

这里是我所调整的:

软件包cern.ieplc.controller; 导入java.net.InetAddress; 导入java.net.InetSocketAddress; 导入java.net.ServerSocket; 导入java.net.Socket; 导入java.net.UnknownHostException;

package cern.ieplc.controller; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException;

导入org.apache.log4j.Logger;

import org.apache.log4j.Logger;

import java.io.BufferedReader; 导入java.io.IOException; 导入java.io.InputStreamReader; 导入java.io.OutputStream;

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream;

public class ApplicationInstanceManager {

    public interface ApplicationInstanceListener {
        public void newInstanceCreated();
    }

    private static final Logger log = Logger.getLogger(CustomLock.class);

    private static ApplicationInstanceListener subListener;

    /** Randomly chosen, but static, high socket number */
    public static final int SINGLE_INSTANCE_NETWORK_SOCKET = 44331;

    /** Must end with newline */
    public static final String SINGLE_INSTANCE_SHARED_KEY = "$$NewInstance$$\n";

    private static ServerSocket socket;
    /**
     * Registers this instance of the application.
     *
     * @return true if first instance, false if not.
     */
    public static boolean registerInstance() {
        // returnValueOnError should be true if lenient (allows app to run on network error) or false if strict.
        boolean returnValueOnError = true;
        // try to open network socket
        // if success, listen to socket for new instance message, return true
        // if unable to open, connect to existing and send new instance message, return false
        try {
            socket = new ServerSocket(SINGLE_INSTANCE_NETWORK_SOCKET, 10, InetAddress.getByAddress(new byte[]{127,0,0,1}));
            socket.setReuseAddress(true);//allows the socket to be bound even though a previous connection is in a timeout state.
            socket.bind(new InetSocketAddress(SINGLE_INSTANCE_NETWORK_SOCKET));
            log.debug("Listening for application instances on socket " + SINGLE_INSTANCE_NETWORK_SOCKET);
            Thread instanceListenerThread = new Thread(new Runnable() {
                public void run() {
                    boolean socketClosed = false;
                    while (!socketClosed) {
                        if (socket.isClosed()) {
                            socketClosed = true;
                        } else {
                            try {
                                Socket client = socket.accept();
                                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                                String message = in.readLine();
                                if (SINGLE_INSTANCE_SHARED_KEY.trim().equals(message.trim())) {
                                    log.debug("Shared key matched - new application instance found");
                                    fireNewInstance();
                                }
                                in.close();
                                client.close();
                            } catch (IOException e) {
                                socketClosed = true;
                            }
                        }
                    }
                }
            });
            instanceListenerThread.start();
            // listen
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
            return returnValueOnError;
        } catch (IOException e) {
            log.debug("Port is already taken.  Notifying first instance.");
            try {
                Socket clientSocket = new Socket(InetAddress.getByAddress(new byte[]{127,0,0,1}), SINGLE_INSTANCE_NETWORK_SOCKET);
                OutputStream out = clientSocket.getOutputStream();
                out.write(SINGLE_INSTANCE_SHARED_KEY.getBytes());
                out.close();
                clientSocket.close();
                log.debug("Successfully notified first instance.");
                return false;
            } catch (UnknownHostException e1) {
                log.error(e.getMessage(), e);
                return returnValueOnError;
            } catch (IOException e1) {
                log.error("Error connecting to local port for single instance notification");
                log.error(e1.getMessage(), e1);
                return returnValueOnError;
            }

        }
        return true;
    }

    public static void setApplicationInstanceListener(ApplicationInstanceListener listener) {
        subListener = listener;
    }

    private static void fireNewInstance() {
        if (subListener != null) {
            subListener.newInstanceCreated();
        }
    }

    public static void closeInstance() {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                log.error("Error while closing the socket");
            }
        }
    }
}

我尝试了代码,在Linux下它确实运行良好.如果我关闭该应用程序(甚至试图杀死它),套接字将立即释放,并且我可以启动一个新的应用程序! 不幸的是,在窗户下觉得不是那么容易.一旦分配了资源就永远不会释放.如果我关闭软件,则在关闭我的部分之前,我将无法再次启动它.

I tryed the code and it works really well under Linux. if i close the application (even trying to kill it) the socket is immediatly released and i can launch a new application! Unfortunatelly under windows thinks are not so easy. once the resource is allocated is never released. if i close the software i will not be able to launch it again till i close my section.

关于如何更好地修复代码以使其在Windows下工作的任何想法. 我可以用一个关闭钩子至少捕获正常的关闭. 真的不知道wat应该做些什么,以防他的处理以意外的方式终止.

Any idea about how fix nicelly the code to make it works under windows. I tought i could use a shut down hook to catch at least the normal shutting down. Do not really know instead wat to do in case he process terminates in an unexpected way.

这里,我在SW TCPView上附加了一个打印屏幕,该屏幕显示了Java如何保持端口打开:

Here i attach a print screen done over the SW TCPView that shoes how the port is kept open by java:

我尝试实现一个简单得多的版本.仍然是同样的问题.在Windows下,资源不会释放.

I tryed implementing a much simpler version. still the same problem. under windows the resources are not released.

这是第二个代码:

import java.net.ServerSocket;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.io.IOException;
import java.net.BindException;

class MyApplication{
    public static ServerSocket serverSocket;
    public static void main(String as[])
    {
        try
        {
            //creating object of server socket and bind to some port number
            serverSocket = new ServerSocket(15486);
            ////do not put common port number like 80 etc.
            ////Because they are already used by system
            JFrame jf = new JFrame();
            jf.setVisible(true);
            jf.setSize(200, 200);
        }
        catch (BindException exc)
        {
            JOptionPane.showMessageDialog(null, "Another instance of this application is already running.", "Error", JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
        catch (IOException exc)
        {
            JOptionPane.showMessageDialog(null, "Another instance of this application is already running.", "Error", JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
    }
}

有些想法不能正确解决. 如果我也将shutdown代码也插入了以下代码中,它将不起作用:

There is somethink that does not clse properly. It does not work if i put in the shutdown hook the followin code as well:

//关闭服务器

try{
    serverSocket.close();
}catch (IOException e) {
    e.printStackTrace();
}

预先感谢

推荐答案

尝试

ServerSocket socket = new ServerSocket();
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(port));

http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#setReuseAddress%28boolean%29

当TCP连接关闭时,连接可能会在连接关闭后的一段时间内保持超时状态(通常称为TIME_WAIT状态或2MSL等待状态).对于使用众所周知的套接字地址或端口的应用程序,如果在超时状态下存在涉及套接字地址或端口的连接,则可能无法将套接字绑定到所需的SocketAddress.

When a TCP connection is closed the connection may remain in a timeout state for a period of time after the connection is closed (typically known as the TIME_WAIT state or 2MSL wait state). For applications using a well known socket address or port it may not be possible to bind a socket to the required SocketAddress if there is a connection in the timeout state involving the socket address or port.

在使用bind(SocketAddress)绑定套接字之前启用SO_REUSEADDR,即使先前的连接处于超时状态,也可以绑定套接字.

Enabling SO_REUSEADDR prior to binding the socket using bind(SocketAddress) allows the socket to be bound even though a previous connection is in a timeout state.

这篇关于带套接字的Java单实例软件.在Windows下关闭套接字的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆