许多程序只允许启动单个实例,比如我们常用的MSN、OUTLOOK等等。单实例有很多好处,其中最重要一点即是安全。想像一下多人同时修改同一文件的危险,就如我们在J2EE应用程序中所做的那样,要尽可能考虑到多用户同时访问的问题。
前些日子用SWING为朋友写了一个程序,其中就有不可同时启动多个系统实例的要求。由于没有一个好的思路,所以走了很多弯路。现在终于解决了,积累了一些心德想与大家分享。
初遇该问题时,我首先想到的是进程。当程序启动时判断操作系统中是否存在该进程,如果存在就退出启动,否则启动程序。这个方法在VB或C语言中可以通过调用WIN32 API来实现。在JAVA中,要想实现该方法或许还要借助C的力量。(对于JAVA如何捕获进程,还请批评指正。)
放弃了第一种方法,想到了弱智的方法--配置文件。当系统第一次启动时将标识设置为启动中,退出时将标识设置为未启动。但很快就发现,当非法关闭程序(比如关机时未及时关闭程序)后,我们的程序便永远长眠了。
其实,在该程序中,最限制我们思路的便是"单机版"这三个字。它给我们的印象是仅供一台机器单独使用,与网络无关的。因此,我们很难将思路整理到服务器与客户机中去。但要解决该问题恰恰要用到服务器与客户机的概念。想像一下我们平时启动电脑的步骤,首先按下加电,有了电,电脑才能启动。没错,只有当电脑未加电,也就是说当我们首次启动电脑时才会做这个动作,而且这个动作在一段时间内只会做一次。若要重新加电,就必需先断电。现在回到我们的程序,有了这个思路,留给我们的问题就是谁来充当电的角色呢?没错,就是之前提到过的服务器。程序首次启动时首先连接指定端口的服务器,发现服务器并未启动,于是启动服务器,启动程序。当程序复数启动时,再次连接服务器,这时发现服务器已经启动了,于是就终止启动。代码如下:
public class Console {
/**端口号*/
private static int iPort = 50000;
/**主窗口*/
JFrame frame = null;
/**
* 系统入口
* @param String[] args
* */
public static void main(String[] args) throws Exception {
Socket socket = null; //客户端连接器
Thread thread = null; //启动服务器的线程
try {
//连接服务器
//如果服务器未启动则抛异常
(socket = new Socket("localhost", iPort)).close();
//如果服务器已经启动则退出系统
System.exit(0);
} catch (Exception e) {}//未做处理
//如果服务器未启动则在新的线程中启动服务器
(thread = new Thread(new Server())).setDaemon(true);
//开始线程
thread.start();
//启动主程序
frame = new JFrame("学海无涯");
frame.setVisible(true);
}
/**
* 端口监听服务器端运行
* @author hiswing
*/
static class Server implements Runnable {
public final void run() {
ServerSocket serversocket = null;
//查找没有占用的端口
while (iPort < 60000) {
try{
serversocket = new ServerSocket(iPort);
}catch(Exception ex){
iPort++;
}
break;
}
try {
do {
//监听客户端是否有连接
serversocket.accept();
//窗口在任务栏闪动
if(frame.getExtendedState() == 1) {
frame.setExtendedState(0);
}
if(frame.getExtendedState() != 1) {
frame.toFront();
frame.requestFocus();
frame.repaint();
}
} while(true);
} catch (Exception ex) {
//不做处理
}
}
}
}
由于没有好的思路,使我们在编程中走了许多弯路,浪费了宝贵的时间。都说软件是智慧的结晶,一点没错。