教你利用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自行關閉服務器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!