基於Socket的服務端多執行緒模式——服務端和客戶端程式碼
阿新 • • 發佈:2018-12-21
本文程式碼來源於《實戰java高併發程式設計》葛一鳴 郭超 著
學習這本書的過程中,感覺這一部分比較重要,自己做下總結,也希望能給大家提供些幫助。
本程式碼模擬簡單的Echo伺服器,對於Echo伺服器,他會讀取客戶端的一個輸入,並將這個輸入原封不動地返回給客戶端。雖然例項很簡單,但麻雀雖小五臟俱全,需要一套完整的Socket處理機制。適合拿來學習。下面貼出本例程式碼。
1、服務端程式碼:
伺服器會為每一個客戶端連線啟動一個執行緒,這個新的執行緒會全心全意為這個客戶端服務。同時,為了接受客戶端連線,伺服器還會額外使用一個派發執行緒,如上圖:
package socket.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * * @author FHY * 多執行緒應用的服務端程式碼 * */ public class MultiThreadEchoServer { //建立一個執行緒池,不限制連線的執行緒數量 public static ExecutorService tp = Executors.newCachedThreadPool(); static class HandleMsg implements Runnable{ Socket clientSocket; public HandleMsg(Socket clientSocket){ this.clientSocket = clientSocket; } @Override public void run() { BufferedReader is = null; PrintWriter os = null; try{ //輸入流 (使用了裝飾模式) is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); //輸出流 os = new PrintWriter(clientSocket.getOutputStream(), true); String inputLine = null; long b = System.currentTimeMillis(); while ((inputLine = is.readLine())!= null ){ os.println(inputLine); } long e = System.currentTimeMillis(); System.out.println("Spend: "+(e-b)); }catch(IOException e){ e.printStackTrace(); }finally{ try{ if(is != null) is.close(); if(os != null) os.close(); clientSocket.close(); }catch(Exception e){ e.printStackTrace(); } } } } public static void main(String[] args) { ServerSocket echoSocket = null; Socket clientSocket = null; try{ //設定服務端的埠號 echoSocket = new ServerSocket(8000); }catch(IOException e){ System.out.println(e); } while(true){ try{ //接受客戶端 clientSocket = echoSocket.accept(); System.out.println(clientSocket.getRemoteSocketAddress() + " connet!"); tp.execute(new HandleMsg(clientSocket)); }catch(IOException e){ System.out.println(e); } } } }
這就是一個支援多執行緒的服務端的核心內容。它的特點是,在相同可支援的執行緒範圍內,可以儘量多地支援客戶端的數量,同時和單執行緒伺服器相比,它可以更好的使用多核CPU。
2、客戶端程式碼:
package socket.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; /** * * @author FHY * 多執行緒應用的客戶端程式碼 * */ public class ClientSocketDemo { public static void main(String[] args) { Socket client = null; PrintWriter writer = null; BufferedReader reader = null; try{ client = new Socket(); //客戶端連線到服務端 client.connect(new InetSocketAddress("localhost", 8000), 1000*6); writer = new PrintWriter(client.getOutputStream(), true); writer.println("Hello!"); writer.flush(); reader = new BufferedReader(new InputStreamReader(client.getInputStream())); System.out.println("from server:" +reader.readLine()); }catch(UnknownHostException e){ e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally { try{ if(writer != null) writer.close(); if(reader != null) reader.close(); if(client != null) client.close(); }catch(IOException e){ System.out.println(e); } } } }
總結:這種多執行緒的伺服器開發模式是及其常用的,對於絕大多數應用來說,這種模式可以很好地工作,但是如果你想讓程式工作地更加有效,就必須知道這種模式的一個重大弱點——那就是它傾向於讓CPU進行IO等待。
使用java的NIO就可以將上面的網路IO等待時間從業務處理執行緒中抽取出來。