我需要强制我的 Java 应用程序使用单个实例运行。我在这个链接上发现了这段非常好的代码,它使用套接字而不是使用文件系统来解决问题。
这是我调整的:
包cern.ieplc.controller;导入java.net.InetAddress;导入 java.net.InetSocketAddress;导入 java.net.ServerSocket;导入 java.net.Socket;导入 java.net.UnknownHostException;
导入 org.apache.log4j.Logger;
导入 java.io.BufferedReader;导入 java.io.IOException;导入 java.io.InputStreamReader;导入 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 下运行得非常好。如果我关闭应用程序(甚至试图杀死它),套接字会立即释放,我可以启动一个新应用程序!可惜windows下想起来没那么容易。一旦分配了资源,就永远不会释放。如果我关闭该软件,我将无法再次启动它,直到我关闭我的部分。
关于如何很好地修复代码以使其在 Windows 下工作的任何想法。我认为我可以使用关闭钩子来捕捉至少正常的关闭。如果他的进程以意想不到的方式终止,真的不知道该怎么做。
在这里,我附上了一个通过 SW TCPView 完成的打印屏幕,说明 Java 如何保持端口打开:
我尝试实现一个更简单的版本。仍然是同样的问题。在windows下,资源不会被释放。
这是第二个代码:
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);
}
}
}
有些想法没有正确关闭。如果我也将以下代码放入关闭挂钩,它不起作用:
//关闭服务器
try{
serverSocket.close();
}catch (IOException e) {
e.printStackTrace();
}
提前致谢