带套接字的Java单实例软件.在Windows下关闭套接字的问题 [英] Java single instance software with socket. issue in closing socket under 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屋!