教你利用JAVA实现可以自行关闭服务器的方法

 更新时间:2021年06月29日 15:32:20   作者:徐徐大嫂  
今天给大家带来的是关于Java的相关知识,文章围绕着利用JAVA实现可以自行关闭服务器的方法展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下

JAVA实现可以自行关闭的服务器

普通实现的服务器都无法关闭自身,只有依靠操作系统来强行终止服务程序。这种强行终止服务程序的方式尽管简单方便,但会导致服务器中正在执行的任务突然中断。如果服务器处理的任务非常重要,不允许被突然中断,应该由服务器自身在恰当的时刻关闭自己

代码如下:

  • EchoServer类
package ShutdownServer;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

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 serverSocketShutdown;
    private boolean isShutdown=false; //服务器是否已经关闭

    private Thread shutdownThread=new Thread(){
        //负责关闭服务器的线程
        public void run(){
            while(!isShutdown){
                Socket socketForShutdown=null;
                try{
                    socketForShutdown=serverSocketShutdown.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();

                        //等待关闭线程池,每次等待的超时时间为30s
                        //当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行。
                        //如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,那么awaitTermination()返回true。执行分线程已结束
                        //如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,那么awaitTermination()返回false。不执行分线程已结束
                        //如果等待时间没有超过指定时间,等待!
                        //可以用awaitTermination()方法来判断线程池中是否有继续运行的线程。
                        while(!executorService.isTerminated())
                            executorService.awaitTermination(30, TimeUnit.SECONDS);
                            //关闭与EchoClient客户通信的ServerSocket
                            serverSocket.close();
                            long endTime=System.currentTimeMillis();
                            socketForShutdown.getOutputStream().write(("服务器关闭,"+"关闭服务器用了"+(endTime-beginTime)+"ms\r\n").getBytes());
                            socketForShutdown.close();
                            serverSocketShutdown.close();
                            System.out.println("服务器关闭");
                    }
                    else {
                        socketForShutdown.getOutputStream().write("错误的命令\r\n".getBytes());
                        socketForShutdown.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    };

    public EchoServer() throws IOException {
        serverSocket=new ServerSocket(port);
        //设定等待客户连接的超时时间为60s
        serverSocket.setSoTimeout(60000);
        serverSocketShutdown=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 {
                //可能会抛出SocketTimeoutException和SocketException
                socket=serverSocket.accept();
                //把等待客户发送数据的超时时间设为60s
                socket.setSoTimeout(60000);
                //可能会抛出RejectedExecutionException
                executorService.execute(new Handler(socket));
            }catch (SocketTimeoutException e){
                //不必处理等待客户连接时出现的异常
            }catch (RejectedExecutionException e) {
                try {
                    if (socket != null)
                        socket.close();
                } catch (IOException ex) {
                    return;
                }
            }catch (SocketException e){
                if (e.getMessage().indexOf("socket closed")!=-1)
                    return;
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException { //main方法抛出异常,异常直接交给虚拟机,虚拟机直接结束异常
        new EchoServer().service();
    }
}

//负责与单个客户通信的任务
class Handler implements Runnable{
    private Socket socket;
    public Handler(Socket socket){
        this.socket=socket;
    }

    private PrintWriter getWriter(Socket socket) throws IOException{
        OutputStream socketOut=socket.getOutputStream();
        return new PrintWriter(socketOut,true);
    }
    private BufferedReader getReader(Socket socket) throws IOException{
        InputStream socketIn=socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketIn));
    }
    public String echo(String msg){
        return "echo: "+msg;
    }

    @Override
    public void run() {
        try{
            System.out.println("New connection accepted "+socket.getInetAddress()+":"+socket.getPort());
            BufferedReader br=getReader(socket);
            PrintWriter pw=getWriter(socket);

            String msg=null;
            //接收和发送数据,直到通信结束
            while((msg=br.readLine())!=null){
                System.out.println("from "+socket.getInetAddress()+":"+socket.getPort()+">"+msg);
                pw.println(echo(msg));
                if (msg.equals("bye"))
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try{
                if (socket!=null)
                    socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}
  • AdminClient类(负责向EchoServer发送“shutdown”命令,关闭服务器)
package ShutdownServer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class AdminClient {
    public static void main(String[] args){
        Socket socket=null;
        try{
            socket=new Socket("localhost",8001);
            //发送关闭命令
            OutputStream socketOut=socket.getOutputStream();
            //Scanner scanner=new Scanner(System.in);
            //String order=scanner.next();
            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 (Exception e) {
            e.printStackTrace();
        }finally {
            try{
                if (socket!=null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • Client类(客户,与服务器进行通讯)
package ShutdownServer;

import java.io.*;
import java.net.Socket;

public class Client {
    private String host="localhost";
    private int port=8000;
    private Socket socket;

    public Client() throws IOException {
        socket=new Socket(host,port);
    }

    private PrintWriter getWriter(Socket socket) throws IOException{
        OutputStream socketOut=socket.getOutputStream();
        return new PrintWriter(socketOut,true);
    }

    private BufferedReader getReader(Socket socket) throws IOException{
        InputStream socketIn=socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketIn));
    }

    public void talk() throws IOException{
        try{
            BufferedReader br=getReader(socket);
            PrintWriter pw=getWriter(socket);
            BufferedReader localReader=new BufferedReader(new InputStreamReader(System.in));
            String msg=null;
            while((msg=localReader.readLine()) != null){
                pw.println(msg);
                System.out.println(br.readLine());

                if (msg.equals("bye")){
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        finally {
            try{
                socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String args[]) throws IOException {
        new Client().talk();
    }
}

shutdownThread线程负责关闭服务器,它一直监听8001端口,如果接收到了AdminClient发送的“shutdown”命令,就把isShutdown设置为true。

在关闭服务器时,我们使用了最常用的方法,先调用线程池的shutdown()方法,接着调用线程池的awaitTermination()方法。

executorService.shutdown();

                        //等待关闭线程池,每次等待的超时时间为30s
                        //当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行。
                        //如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,那么awaitTermination()返回true。执行分线程已结束
                        //如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,那么awaitTermination()返回false。不执行分线程已结束
                        //如果等待时间没有超过指定时间,等待!
                        //可以用awaitTermination()方法来判断线程池中是否有继续运行的线程。
                        while(!executorService.isTerminated())
                            executorService.awaitTermination(30, TimeUnit.SECONDS);

在线程池执行了shutdown()方法后,线程池不会在接收新的任务,同时该线程因为调用awaitTermination()方法而发生阻塞,直到线程池中所有线程的任务执行完毕,该线程才会继续向下

运行结果

先运行EchoServer,Client,AdminClient后,再开启一客户程序Client1,显示Client1无法被加入线程池

  • EchoServer(只显示连接了Client,未连接Client1)

在这里插入图片描述

  • Client

在这里插入图片描述

  • Client2(向服务器发送消息,收到null)

在这里插入图片描述

  • AdminClient(在Client没有运行结束时,被阻塞)

在这里插入图片描述

当Client输入“bye”结束运行后,AdminClient关闭服务器

  • Client类

在这里插入图片描述

  • EchoServer类

在这里插入图片描述

  • AdminClient类

在这里插入图片描述

参考Java网络编程核心技术详解

到此这篇关于教你利用JAVA实现可以自行关闭服务器的方法的文章就介绍到这了,更多相关JAVA自行关闭服务器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java实现PPT转化为PDF

    java实现PPT转化为PDF

    这篇文章主要为大家详细介绍了java实现PPT转化为PDF的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 解析HikariCP一百行代码轻松掌握多线程

    解析HikariCP一百行代码轻松掌握多线程

    这篇文章主要为大家介绍了HikariCP一百行代码解析,轻松掌握多线程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 使用ObjectMapper解析json不用一直new了

    使用ObjectMapper解析json不用一直new了

    这篇文章主要为大家介绍了使用ObjectMapper解析json不用一直new了的方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • JAVA面试题 简谈你对synchronized关键字的理解

    JAVA面试题 简谈你对synchronized关键字的理解

    这篇文章主要介绍了JAVA面试题 请谈谈你对Sychronized关键字的理解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 关于@PostConstruct、afterPropertiesSet和init-method的执行顺序

    关于@PostConstruct、afterPropertiesSet和init-method的执行顺序

    这篇文章主要介绍了关于@PostConstruct、afterPropertiesSet和init-method的执行顺序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java IDEA集成开发工具中英文切换图文教程

    Java IDEA集成开发工具中英文切换图文教程

    相信很多小伙伴们刚接触IDEA时,看到一堆英文界面不知道如何下手,这篇文章主要给大家介绍了关于Java IDEA集成开发工具中英文切换的相关资料,需要的朋友可以参考下
    2024-04-04
  • Spring Cloud OAuth2实现自定义token返回格式

    Spring Cloud OAuth2实现自定义token返回格式

    Spring Security OAuth的token返回格式都是默认的,但是往往这个格式是不适配系统。本文将用一个接口优雅的实现 Spring Cloud OAuth2 自定义token返回格式,需要的可以参考一下
    2022-06-06
  • Java 设计模式之适配器模式详解

    Java 设计模式之适配器模式详解

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
    2021-11-11
  • Java经典快排思想以及快排的改进讲解

    Java经典快排思想以及快排的改进讲解

    今天小编就为大家分享一篇关于Java经典快排思想以及快排的改进讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Mybatis和orcale update语句中接收参数为对象的实例代码

    Mybatis和orcale update语句中接收参数为对象的实例代码

    Mybatis的 mapper.xml 中 update 语句使用 if 标签判断对像属性是否为空值。本文重点给大家介绍Mybatis和orcale update语句中接收参数为对象的实例代码,需要的朋友参考下吧
    2017-09-09

最新评论