【Netty入門和實踐】1.傳統的socket分析
阿新 • • 發佈:2019-01-25
我們知道,使用Java進行TCP/UDP協議的網路通訊一般使用Java的Net包下的Socket服務進行編寫,有Server服務端和Client客戶端,服務端用於監聽客戶端的連線和接收客戶端發來的資訊,而客戶端可以接收服務端傳送的資訊,形成多個客戶端與一個服務端連線,各個客戶端可以相互通訊的機制。
下面我們就編寫一個簡單的基於Socket服務的TCP協議傳輸資訊的服務端,然後來進行分析。
在Eclipse中新建一個Java工程,名為“IOServer”即是服務端:
然後在src下面建立一個cn.com.io.old包,下面建立一個名為“Old_IO_Server”的類:
在該類中編寫服務端程式碼:
可以看到在客戶端沒有連線進來的時候,服務端會在server.accept();的位置阻塞等待。
然後使用Windows的CMD控制檯的telnet進行客戶端連線:
可以看到程式向下走了:
當程式走到read = input.read(bytes);發生了阻塞等待,等待客戶端傳輸資訊:
此時在阻塞過程中,意味著這條執行緒是被這個Socket一直佔用著的,其它的Socket不能進來。(再次開啟一個CMD控制檯使用telnet,可以發現進不來)
當我們在CMD中輸入資訊(Ctrl+]進入客戶端,send發信息),可以看到服務端收到了資訊:
想要服務端處理多個客戶端的資訊,就需要為每一個客戶端分配一個執行緒。下面我們修改一下服務端:
現在使用兩個客戶端連線10010埠,可以就看到兩個客戶端的資訊都可以接收到:
那麼我們是不是就可以使用這種結構實現一個長連線伺服器呢?這裡還是有一個很大的弊端,因為目前我們每個客戶端都為其分配了一個執行緒去執行,如果有一萬個客戶端進來,我們就要分配一萬個執行緒給客戶端使用,這樣的資源消耗是十分巨大的。就好像一個飯店每進來一個客人,就為其分配一個服務員給他服務一樣,對於飯店來說服務員的開銷也是巨大的。
基於以上幾點,傳統的Socket服務實現長連線服務是不合適的,但是可以實現短連線的伺服器,如老版本的tomcat。
總結:
傳統IO傳輸的特點:
(1)至少兩處阻塞點(等待客戶端連線,等待客戶端傳送資訊)
(2)單執行緒下只能有一個客戶端連線
(3)使用執行緒池可以連線多個客戶端,但是十分消耗效能。
下面我們就編寫一個簡單的基於Socket服務的TCP協議傳輸資訊的服務端,然後來進行分析。
在Eclipse中新建一個Java工程,名為“IOServer”即是服務端:
然後在src下面建立一個cn.com.io.old包,下面建立一個名為“Old_IO_Server”的類:
在該類中編寫服務端程式碼:
我們在mian函式的第一行打一個斷點,使用Debug模式執行該程式:package cn.com.io.old; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /*** * 傳統socket服務端 */ public class Old_IO_Server { public static void main(String[] args) throws IOException { //建立Socket服務,監聽10010埠 ServerSocket server = new ServerSocket(10010); System.out.println("服務端啟動!"); while(true){ //獲取一個套接字(阻塞) final Socket socket = server.accept(); System.out.println("出現一個新客戶端!"); //業務處理 handler(socket); } } /** * 讀取資料 * @param socket * @throws Exception * */ private static void handler(Socket socket){ try { byte [] bytes = new byte[1024]; InputStream input = socket.getInputStream(); int read = 0; while(read!=-1){ //讀取資料(阻塞) read = input.read(bytes); System.out.println(new String(bytes,0,read)); } } catch (Exception e) { e.printStackTrace(); }finally{ try { System.out.println("socket 關閉"); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
可以看到在客戶端沒有連線進來的時候,服務端會在server.accept();的位置阻塞等待。
然後使用Windows的CMD控制檯的telnet進行客戶端連線:
可以看到程式向下走了:
當程式走到read = input.read(bytes);發生了阻塞等待,等待客戶端傳輸資訊:
此時在阻塞過程中,意味著這條執行緒是被這個Socket一直佔用著的,其它的Socket不能進來。(再次開啟一個CMD控制檯使用telnet,可以發現進不來)
當我們在CMD中輸入資訊(Ctrl+]進入客戶端,send發信息),可以看到服務端收到了資訊:
想要服務端處理多個客戶端的資訊,就需要為每一個客戶端分配一個執行緒。下面我們修改一下服務端:
可以看到我們建立了一個快取執行緒池,當服務端新連線了一個客戶端的時候,就建立一個新的執行緒為客戶端進行服務。package cn.com.io.old; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /*** * 傳統socket服務端 */ public class Old_IO_Server { public static void main(String[] args) throws IOException { //建立一個快取執行緒池 ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); //建立Socket服務,監聽10010埠 ServerSocket server = new ServerSocket(10010); System.out.println("服務端啟動!"); while(true){ //獲取一個套接字(阻塞) final Socket socket = server.accept(); System.out.println("出現一個新客戶端!"); //線上程池為新客戶端開一個執行緒 newCachedThreadPool.execute(new Runnable() { @Override public void run() { //業務處理 handler(socket); } }); } } /** * 讀取資料 * @param socket * @throws Exception * */ private static void handler(Socket socket){ try { byte [] bytes = new byte[1024]; InputStream input = socket.getInputStream(); int read = 0; while(read!=-1){ //讀取資料(阻塞) read = input.read(bytes); System.out.println(new String(bytes,0,read)); } } catch (Exception e) { e.printStackTrace(); }finally{ try { System.out.println("socket 關閉"); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
現在使用兩個客戶端連線10010埠,可以就看到兩個客戶端的資訊都可以接收到:
那麼我們是不是就可以使用這種結構實現一個長連線伺服器呢?這裡還是有一個很大的弊端,因為目前我們每個客戶端都為其分配了一個執行緒去執行,如果有一萬個客戶端進來,我們就要分配一萬個執行緒給客戶端使用,這樣的資源消耗是十分巨大的。就好像一個飯店每進來一個客人,就為其分配一個服務員給他服務一樣,對於飯店來說服務員的開銷也是巨大的。
基於以上幾點,傳統的Socket服務實現長連線服務是不合適的,但是可以實現短連線的伺服器,如老版本的tomcat。
總結:
傳統IO傳輸的特點:
(1)至少兩處阻塞點(等待客戶端連線,等待客戶端傳送資訊)
(2)單執行緒下只能有一個客戶端連線
(3)使用執行緒池可以連線多個客戶端,但是十分消耗效能。
使用NIO就可以解決傳統IO的網路傳輸問題,我們下篇進行介紹。