NIO入門之BIO
阿新 • • 發佈:2018-12-23
傳統BIO程式設計
網路程式設計的基本模型是Client-Server模型,也就是兩個程序之間相互通訊,其中服務端提供位置資訊(繫結的IP地址和監聽埠),客戶端通過連線操作向服務端監聽的埠發起連線請求,通過三次握手建立連線,如果連線成功,雙方就可以通過網路套接字(Socket)進行通訊。
在傳統的BIO程式設計中,ServerSocket負責繫結IP地址,啟動埠監聽,Socket負責發起連線請求,連線成功之後,雙方通過輸入和輸出流進行同步阻塞通訊。
下面通過TimeServer的一個例子,回顧和熟悉BIO程式設計
BIO通訊模型圖
可以看到再改模型中,有一個Acceptor執行緒負責監聽客戶端的連線,併為每個請求建立一個新的執行緒進行處理。
我們可以發現該模型最大問題就是缺乏彈性伸縮能力,服務端和客戶端執行緒個數是1比1的關係。
BIO的TimeServer
package nio.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * Created by jj on 2018/12/23. */ public class TimeServer { public static void main(String[] args) throws IOException { int port = 8080; if (args != null && args.length >0){ try{ port = Integer.parseInt(args[0]); }catch (NumberFormatException e){ } } ServerSocket server = null; try{ server = new ServerSocket(port); Socket socket = null; while (true){ socket = server.accept(); new Thread(new TimeServerHandler(socket)).start(); } }finally { if (server!= null){ server.close(); server = null; } } } }
如果沒有客戶端請求,則阻塞在server.accept操作上,如果有,則建立一個TimeServerHandler的Runnable執行緒,處理客戶端的Socket鏈路
下面,我們看一下TimeServerHandler
public class TimeServerHandler implements Runnable{ private Socket socket; public TimeServerHandler(Socket socket) { this.socket = socket; } public void run() { BufferedReader in = null; PrintWriter out = null; try{ in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(),true); String curentTime = null; String body = null; while (true){ body = in.readLine(); if (body == null) break; System.out.println("the time server receive order:" + body); curentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?new Date( System.currentTimeMillis() ).toString():"BAD ORDER"; out.println(curentTime); } } catch (Exception e) { if (in != null){ try { in.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (out != null){ out.close(); out = null; } if (this.socket !=null){ try { this.socket.close(); } catch (IOException e1) { e1.printStackTrace(); } this.socket = null; } } } }
可以看到run中的功能為讀取客戶端請求,並通過PrintWriter返回給客戶端相應。
下面我們看一下客戶端的程式碼
public class TimeClient {
public static void main(String[] args) throws IOException {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
}
}
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try{
socket = new Socket("127.0.0.1",port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
out.println("QUERY TIME ORDER");
String resp = in.readLine();
System.out.print(resp);
}finally {
if (out != null){
out.close();
out = null;
}
if (in != null){
in.close();
in = null;
}
if (socket != null){
socket.close();
socket = null;
}
}
}
}