频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

3.7 关闭服务器

作者: 孙卫琴 出处:电子工业出版社  (  ) 砖  (  ) 好  评论 ( ) 条  进入论坛
更新时间:2007-04-26 13:29
关 键 词:Java  网络  编程  Java网络编程精解
阅读提示:本书结合大量典型的实例,详细介绍了用Java来编写网络应用程序的技术。本书内容包括:Java网络编程的基础知识、套接字编程、非阻塞通信、创建HTTP服务器与客户程序、数据报通信、对象的序列化与反序列化、Java反射机制、RMI框架、JDBC API、JavaMail API、MVC设计模式、安全网络通信、CORBA和Web服务。本文是关闭服务器。
3.7  关闭服务器
前面介绍的EchoServer服务器都无法关闭自身,只有依靠操作系统来强行终止服务器程序。这种强行终止服务器程序的方式尽管简单方便,但是会导致服务器中正在执行的任务被突然中断。如果服务器处理的任务不是非常重要,允许随时中断,则可以依靠操作系统来强行终止服务器程序;如果服务器处理的任务非常重要,不允许被突然中断,则应该由服务器自身在恰当的时刻关闭自己。
本节介绍的EchoServer服务器就具有关闭自己的功能。它除了在8000端口监听普通客户程序EchoClient的连接外,还会在8001端口监听管理程序AdminClient的连接。当EchoServer服务器在8001端口接收到了AdminClient发送的“shutdown”命令时,EchoServer就会开始关闭服务器,它不会再接收任何新的EchoClient进程的连接请求,对于那些已经接收但是还没有处理的客户连接,则会丢弃与该客户的通信任务,而不会把通信任务加入到线程池的工作队列中。另外,EchoServer会等到线程池把当前工作队列中的所有任务执行完,才结束程序。
如例程3-10所示是EchoServer的源程序,其中关闭服务器的任务是由shutdown- Thread线程来负责的。
例程3-10  EchoServer.java(具有关闭服务器的功能)
package multithread4;
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class EchoServer {
private int port=8000;
private ServerSocket serverSocket;
private ExecutorService executorService;     //线程池
private final int POOL_SIZE=4;       //单个CPU时线程池中工作线程的数目

private int portForShutdown=8001;      //用于监听关闭服务器命令的端口
private ServerSocket serverSocketForShutdown;
private boolean isShutdown=false;     //服务器是否已经关闭
  private Thread shutdownThread=new Thread(){     //负责关闭服务器的线程
public void start(){
this.setDaemon(true);       //设置为守护线程(也称为后台线程)
super.start();
}
public void run(){
while (!isShutdown) {
Socket socketForShutdown=null;
try {
socketForShutdown= serverSocketForShutdown.accept();
BufferedReader br = new BufferedReader(
new InputStreamReader(socketForShutdown.getInputStream()));
String command=br.readLine();
if(command.equals("shutdown")){
long beginTime=System.currentTimeMillis();
socketForShutdown.getOutputStream().write("服务器正在关闭\r\n".getBytes());
isShutdown=true;
//请求关闭线程池
//线程池不再接收新的任务,但是会继续执行完工作队列中现有的任务
executorService.shutdown(); 

//等待关闭线程池,每次等待的超时时间为30秒
while(!executorService.isTerminated())
executorService.awaitTermination(30,TimeUnit.SECONDS);

serverSocket.close(); //关闭与EchoClient客户通信的ServerSocket
long endTime=System.currentTimeMillis();
socketForShutdown.getOutputStream().write(("服务器已经关闭,"+
"关闭服务器用了"+(endTime-beginTime)+"毫秒\r\n").getBytes());
socketForShutdown.close();
serverSocketForShutdown.close();

}else{
socketForShutdown.getOutputStream().write("错误的命令\r\n".getBytes());
socketForShutdown.close();

}catch (Exception e) {
e.printStackTrace();
}
}
}
};
  public EchoServer() throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(60000);    //设定等待客户连接的超过时间为60秒
serverSocketForShutdown = new ServerSocket(portForShutdown);
    //创建线程池
executorService= Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() * POOL_SIZE);

shutdownThread.start();     //启动负责关闭服务器的线程
System.out.println("服务器启动");
}

public void service() {
while (!isShutdown) {
Socket socket=null;
try {
socket = serverSocket.accept();
//可能会抛出SocketTimeoutException和SocketException
socket.setSoTimeout(60000);     //把等待客户发送数据的超时时间设为60秒
executorService.execute(new Handler(socket));
//可能会抛出RejectedExecutionException
}catch(SocketTimeoutException e){
//不必处理等待客户连接时出现的超时异常
}catch(RejectedExecutionException e){
try{
if(socket!=null)socket.close();
}catch(IOException x){}
return;
}catch(SocketException e) {
//如果是由于在执行serverSocket.accept()方法时,
//ServerSocket被ShutdownThread线程关闭而导致的异常,就退出service()方法
if(e.getMessage().indexOf("socket closed")!=-1)return;
}catch(IOException e) {
e.printStackTrace();
}
}
}
  public static void main(String args[])throws IOException {
new EchoServer().service();
}
}
/** 负责与单个客户通信的任务,代码与3.6.1节的例程3-5的Handler类相同 */
class Handler implements Runnable{…}
shutdownThread线程负责关闭服务器。它一直监听8001端口,如果接收到了AdminClient发送的“shutdown”命令,就把isShutdown变量设为true。shutdownThread线程接着执行executorService.shutdown()方法,该方法请求关闭线程池,线程池将不再接收新的任务,但是会继续执行完工作队列中现有的任务。shutdownThread线程接着等待线程池关闭:
while(!executorService.isTerminated())
executorService.awaitTermination(30,TimeUnit.SECONDS);  //等待30秒
当线程池的工作队列中的所有任务执行完毕,executorService.isTerminated()方法就会返回true。
shutdownThread线程接着关闭监听8000端口的ServerSocket,最后再关闭监听8001端口的ServerSocket。
shutdownThread线程在执行上述代码时,主线程正在执行EchoServer的service()方法。shutdownThread线程一系列操作会对主线程造成以下影响。
 如果shutdownThread线程已经把isShutdown变量设为true,而主线程正准备执行service()方法的下一轮while(!isShutdown){…}循环时,由于isShutdown变量为true,就会退出循环。
 如果shutdownThread线程已经执行了监听8 000端口的ServerSocket的close()方法,而主线程正在执行该ServerSocket的accept()方法,那么该方法会抛出SocketException。EchoServer的service()方法捕获了该异常,在异常处理代码块中退出service()方法。
 如果shutdownThread线程已经执行了executorService.shutdown()方法,而主线程正在执行executorService.execute(…)方法,那么该方法会抛出Rejected- ExecutionException。EchoServer的service()方法捕获了该异常,在异常处理代码块中退出service()方法。
 如果shutdownThread线程已经把isShutdown变量设为true,但还没有调用监听8 000端口的ServerSocket的close()方法,而主线程正在执行ServerSocket的accept()方法,主线程阻塞60秒后会抛出SocketTimeoutException。在准备执行service()方法的下一轮while(!isShutdown){…}循环时,由于isShutdown变量为true,就会退出循环。
 由此可见,当shutdownThread线程开始执行关闭服务器的操作时,主线程尽管不会立即终止,但是迟早会结束运行。
如例程3-11所示是AdminClient的源程序,它负责向EchoServer发送“shutdown”命令,从而关闭EchoServer。
例程3-11  AdminClient.java
package multithread4;
import java.net.*;
import java.io.*;
public class AdminClient{
public static void main(String args[]){
Socket socket=null;
try{
socket=new Socket("localhost",8001);
//发送关闭命令
OutputStream socketOut=socket.getOutputStream();
socketOut.write("shutdown\r\n".getBytes());

//接收服务器的反馈
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String msg=null;
while((msg=br.readLine())!=null)
System.out.println(msg);
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(socket!=null)socket.close();
}catch(IOException e){e.printStackTrace();}
}
}
}
下面按照以下方式运行EchoServer、EchoClient和AdminClient,以观察EchoServer服务器的关闭过程。EchoClient类的源程序参见本书第1章的1.5.2节的例程1-3。
(1)先运行EchoServer,然后运行AdminClient。EchoServer与AdminClient进程都结束运行,并且在AdminClient的控制台打印如下结果:
服务器正在关闭
服务器已经关闭,关闭服务器用了60毫秒 
(2)先运行EchoServer,再运行EchoClient,然后再运行AdminClient。EchoServer程序不会立即结束,因为它与EchoClient的通信任务还没有结束。在EchoClient的控制台中输入“bye”, EchoServer、EchoClient和AdminClient进程都会结束运行。
(3)先运行EchoServer,再运行EchoClient,然后再运行AdminClient。EchoServer程序不会立即结束,因为它与EchoClient的通信任务还没有结束。不要在EchoClient的控制台中输入任何字符串,过60秒后,EchoServer等待EchoClient的发送数据超时,结束与EchoClient的通信任务,EchoServer和AdminClient进程结束运行。如果在EchoClient的控制台再输入字符串,则会抛出“连接已断开”的SocketException。
【责任编辑:雪花 TEL:(010)68476606-8007】

回书目   上一节   下一节
发表
查看
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
名称:Vista精品应用黄皮书
简介:《Vista精品应用黄皮书》囊括了Vista的各方面内容。此次的精简版,是将里面的内容做了提取,便于用户下载和使用。内容包含了各种Vista的安装与实施、技巧与解析以及各种Vista相关学习文档和相关软件的安全下载。该电子书是了解和应用Vista人员必备的工具手册,并且也是第一本
名称:2006中国IT论坛精品集合
简介:本书由“51CTO论坛推广联盟”制作完成。书中所有内容均来自各联盟成员的论坛(网站)。制作本书的目的是为了集中大家的优势资源,将更多更精彩的内容带给广大技术爱好者。本书是联盟成立以来制作的第一本书。
关键字阅读
频道精选
主编信箱 热线:010-66476606 告诉我们您想看的:专题 文章
关于我们 | 诚聘英才 | 联系我们 | 网站大事 | 意见反馈 | 网站地图
Copyright©2005-2007 51CTO.COM 版权所有