带套接字的 Java 单实例软件。 windows下关闭socket的问题
我需要强制我的 Java 应用程序以单个实例运行。我在这个链接上发现了这段非常好的代码使用socket而不是使用文件系统来解决问题。
这是我调整的:
package 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();
}
提前致谢
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.
here the as i adjusted:
package cern.ieplc.controller;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.log4j.Logger;
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");
}
}
}
}
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.
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.
Here i attach a print screen done over the SW TCPView that shoes how the port is kept open by java:
I tryed implementing a much simpler version. still the same problem. under windows the resources are not released.
Here is the second code:
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);
}
}
}
There is somethink that does not clse properly.
It does not work if i put in the shutdown hook the followin code as well:
// shut down server
try{
serverSocket.close();
}catch (IOException e) {
e.printStackTrace();
}
Thanks in advance
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
尝试
http://download .oracle.com/javase/6/docs/api/java/net/ServerSocket.html#setReuseAddress%28boolean%29
Try
http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#setReuseAddress%28boolean%29
这些说法到底是什么意思?在任何低于 Novell Netware 3.x 的操作系统上,如果进程被终止,其所有资源都会被释放。您有什么证据表明侦听套接字未关闭,以及当您尝试重新启动应用程序时会发生什么?
What exactly do you mean by these statements? If a process is terminated all its resources are released, on any operating system short of Novell Netware 3.x. What evidence do you have that the listening socket isn't being closed, and what happens when you try to relaunch the application?