OSI七層網絡模型css |
TCP/IP四層概念模型 html |
對應網絡協議java |
應用層(Application)web |
應用層chrome |
HTTP、TFTP, FTP, NFS, WAIS、SMTP數據庫 |
表示層(Presentation)apache |
Telnet, Rlogin, SNMP, Gopher編程 |
|
會話層(Session)segmentfault |
SMTP, DNSwindows |
|
傳輸層(Transport) |
傳輸層 |
TCP, UDP |
網絡層(Network) |
網絡層 |
IP, ICMP, ARP, RARP, AKP, UUCP |
數據鏈路層(Data Link) |
數據鏈路層 |
FDDI, Ethernet, Arpanet, PDN, SLIP, PPP |
物理層(Physical) |
IEEE 802.1A, IEEE 802.2到IEEE 802.11 |
TCP | UDP | |
是否鏈接 | 面向鏈接 | 面向非鏈接 |
傳輸可靠性 | 可靠 | 不可靠 |
應用場合 | 傳輸大量的數據,對可靠性要求較高的場合 | 傳送少許數據、對可靠性要求不高的場景 |
速度 | 慢 | 快 |
全稱是Hypertext Transfer Protocol,即超文本傳輸協議。從名字上能夠看出該協議用於規定客戶端與服務端之間的傳輸規則,所傳輸的內容不侷限於文本(HTML 文件, 圖片文件, 查詢結果等)。
Socket的大體位置以下: 爲了便於編程而設想出來的API/編程模型
HTTP是應用層協議.定義的是傳輸數據的內容的規範。 全稱是Hypertext Transfer Protocol,即超文本傳輸協議。從名字上能夠看出該協議用於規定客戶端與服務端之間的傳輸規則,所傳輸的內容不侷限於文本(HTML 文件, 圖片文件, 查詢結果等)。 HTTP對應在傳輸層使用的仍是TCP協議進行通訊.
HTTP是應用層協議.定義的是傳輸數據的內容的規範。
全稱是Hypertext Transfer Protocol,即超文本傳輸協議。從名字上能夠看出該協議用於規定客戶端與服務端之間的傳輸規則,所傳輸的內容不侷限於文本(HTML 文件, 圖片文件, 查詢結果等)。
HTTP對應在傳輸層使用的仍是TCP協議進行通訊.
從圖中能夠看到:
在串行鏈接中,每次交互都要打開關閉鏈接
在持久鏈接中,第一次交互會打開鏈接,交互結束後鏈接並不關閉,下次交互就省去了創建鏈接的過程。
Connection: Keep-Alive
Connection: Keep-Alive
Connection: Keep-Alive
Connection: Keep-Alive
Socket socket=new Socket("127.0.0.1",4700); //向本機的4700端口發出客戶請求 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); //由系統標準輸入設備構造BufferedReader對象 PrintWriter os=new PrintWriter(socket.getOutputStream()); //由Socket對象獲得輸出流,並構造PrintWriter對象 BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); //由Socket對象獲得輸入流,並構造相應的BufferedReader對象 String readline; readline=sin.readLine(); //從系統標準輸入讀入一字符串 while(!readline.equals("bye")){ //若從標準輸入讀入的字符串爲 "bye"則中止循環 os.println(readline); //將從系統標準輸入讀入的字符串輸出到Server os.flush(); //刷新輸出流,使Server立刻收到該字符串 System.out.println("Client:"+readline); //在系統標準輸出上打印讀入的字符串 System.out.println("Server:"+is.readLine()); //從Server讀入一字符串,並打印到標準輸出上 readline=sin.readLine(); //從系統標準輸入讀入一字符串 } //繼續循環 os.close(); //關閉Socket輸出流 is.close(); //關閉Socket輸入流 socket.close(); //關閉Socket }catch(Exception e) { System.out.println("Error"+e); //出錯,則打印出錯信息 }
Socket socket=new Socket("127.0.0.1",4700);
//向本機的4700端口發出客戶請求
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系統標準輸入設備構造BufferedReader對象
PrintWriter os=new PrintWriter(socket.getOutputStream());
//由Socket對象獲得輸出流,並構造PrintWriter對象
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket對象獲得輸入流,並構造相應的BufferedReader對象
String readline;
readline=sin.readLine(); //從系統標準輸入讀入一字符串
while(!readline.equals("bye")){
//若從標準輸入讀入的字符串爲 "bye"則中止循環
os.println(readline);
//將從系統標準輸入讀入的字符串輸出到Server
os.flush();
//刷新輸出流,使Server立刻收到該字符串
System.out.println("Client:"+readline);
//在系統標準輸出上打印讀入的字符串
System.out.println("Server:"+is.readLine());
//從Server讀入一字符串,並打印到標準輸出上
readline=sin.readLine(); //從系統標準輸入讀入一字符串
} //繼續循環
os.close(); //關閉Socket輸出流
is.close(); //關閉Socket輸入流
socket.close(); //關閉Socket
}catch(Exception e) {
System.out.println("Error"+e); //出錯,則打印出錯信息
}
try{ ServerSocket server=null; try{ server=new ServerSocket(4700); //建立一個ServerSocket在端口4700監聽客戶請求 }catch(Exception e) { System.out.println("can not listen to:"+e); //出錯,打印出錯信息 } Socket socket=null; try{ socket=server.accept(); //使用accept()阻塞等待客戶請求,有客戶 //請求到來則產生一個Socket對象,並繼續執行 }catch(Exception e) { System.out.println("Error."+e); //出錯,打印出錯信息 } String line; BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); //由Socket對象獲得輸入流,並構造相應的BufferedReader對象 PrintWriter os=newPrintWriter(socket.getOutputStream()); //由Socket對象獲得輸出流,並構造PrintWriter對象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); //由系統標準輸入設備構造BufferedReader對象 System.out.println("Client:"+is.readLine()); //在標準輸出上打印從客戶端讀入的字符串 line=sin.readLine(); //從標準輸入讀入一字符串 while(!line.equals("bye")){ //若是該字符串爲 "bye",則中止循環 os.println(line); //向客戶端輸出該字符串 os.flush(); //刷新輸出流,使Client立刻收到該字符串 System.out.println("Server:"+line); //在系統標準輸出上打印讀入的字符串 System.out.println("Client:"+is.readLine()); //從Client讀入一字符串,並打印到標準輸出上 line=sin.readLine(); //從系統標準輸入讀入一字符串 } //繼續循環 os.close(); //關閉Socket輸出流 is.close(); //關閉Socket輸入流 socket.close(); //關閉Socket server.close(); //關閉ServerSocket }catch(Exception e){ System.out.println("Error:"+e); //出錯,打印出錯信息 } }
try{
ServerSocket server=null;
try{
server=new ServerSocket(4700);
//建立一個ServerSocket在端口4700監聽客戶請求
}catch(Exception e) {
System.out.println("can not listen to:"+e);
//出錯,打印出錯信息
}
Socket socket=null;
try{
socket=server.accept();
//使用accept()阻塞等待客戶請求,有客戶
//請求到來則產生一個Socket對象,並繼續執行
}catch(Exception e) {
System.out.println("Error."+e);
//出錯,打印出錯信息
}
String line;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket對象獲得輸入流,並構造相應的BufferedReader對象
PrintWriter os=newPrintWriter(socket.getOutputStream());
//由Socket對象獲得輸出流,並構造PrintWriter對象
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系統標準輸入設備構造BufferedReader對象
System.out.println("Client:"+is.readLine());
//在標準輸出上打印從客戶端讀入的字符串
line=sin.readLine();
//從標準輸入讀入一字符串
while(!line.equals("bye")){
//若是該字符串爲 "bye",則中止循環
os.println(line);
//向客戶端輸出該字符串
os.flush();
//刷新輸出流,使Client立刻收到該字符串
System.out.println("Server:"+line);
//在系統標準輸出上打印讀入的字符串
System.out.println("Client:"+is.readLine());
//從Client讀入一字符串,並打印到標準輸出上
line=sin.readLine();
//從系統標準輸入讀入一字符串
} //繼續循環
os.close(); //關閉Socket輸出流
is.close(); //關閉Socket輸入流
socket.close(); //關閉Socket
server.close(); //關閉ServerSocket
}catch(Exception e){
System.out.println("Error:"+e);
//出錯,打印出錯信息
}
}
BasicHttpClientConnectionManager
是個簡單的鏈接管理器,它一次只能管理一個鏈接。儘管這個類是線程安全的,它在同一時間也只能被一個線程使用。
BasicHttpClientConnectionManager
會盡可能重用舊的鏈接來發送後續的請求,而且使用相同的路由。若是後續請求的路由和舊鏈接中的路由不匹配,
BasicHttpClientConnectionManager
就會關閉當前鏈接,使用請求中的路由從新創建鏈接。若是當前的鏈接正在被佔用,會拋出
java.lang.IllegalStateException
異常。
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); // 將最大鏈接數增長到200 cm.setMaxTotal(200); // 將每一個路由基礎的鏈接增長到20 cm.setDefaultMaxPerRoute(20); //將目標主機的最大鏈接數增長到50 HttpHost localhost = new HttpHost("www.yeetrack.com", 80); cm.setMaxPerRoute(new HttpRoute(localhost), 50); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// 將最大鏈接數增長到200
cm.setMaxTotal(200);
// 將每一個路由基礎的鏈接增長到20
cm.setDefaultMaxPerRoute(20);
//將目標主機的最大鏈接數增長到50
HttpHost localhost = new HttpHost("www.yeetrack.com", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
CloseableHttpClient httpClient = <...> httpClient.close();
CloseableHttpClient httpClient = <...>
httpClient.close();
經典阻塞I/O模型的一個主要缺點就是隻有當組側I/O時,socket才能對I/O事件作出反應。當鏈接被管理器收回後,這個鏈接仍然存活,可是卻沒法監控socket的狀態,也沒法對I/O事件作出反饋。若是鏈接被服務器端關閉了,客戶端監測不到鏈接的狀態變化(也就沒法根據鏈接狀態的變化,關閉本地的socket)。
HttpClient爲了緩解這一問題形成的影響,會在使用某個鏈接前,監測這個鏈接是否已通過時,若是服務器端關閉了鏈接,那麼鏈接就會失效。這種過期檢查並非100%有效,而且會給每一個請求增長10到30毫秒額外開銷。惟一一個可行的,且does not involve a one thread per socket model for idle connections的解決辦法,是創建一個監控線程,來專門回收因爲長時間不活動而被斷定爲失效的鏈接。這個監控線程能夠週期性的調用ClientConnectionManager
類的closeExpiredConnections()
方法來關閉過時的鏈接,回收鏈接池中被關閉的鏈接。它也能夠選擇性的調用ClientConnectionManager
類的closeIdleConnections()
方法來關閉一段時間內不活動的鏈接。
在NIO實現的Connector中,處理請求的主要實體是NIoEndpoint對象。NIoEndpoint中除了包含Acceptor和Worker外,還使用了Poller,處理流程以下圖所示(圖片來源:http://gearever.iteye.com/blog/1844203)。
圖6-2
Acceptor接收socket後,不是直接使用Worker中的線程處理請求,而是先將請求發送給了Poller,而Poller是實現NIO的關鍵。Acceptor向Poller發送請求經過隊列實現,使用了典型的生產者-消費者模式。在Poller中,維護了一個Selector對象;當Poller從隊列中取出socket後,註冊到該Selector中;而後經過遍歷Selector,找出其中可讀的socket,並使用Worker中的線程處理相應請求。與BIO相似,Worker也能夠被自定義的線程池代替。
經過上述過程能夠看出,在NIoEndpoint處理請求的過程當中,不管是Acceptor接收socket,仍是線程處理請求,使用的仍然是阻塞方式;但在「讀取socket並交給Worker中的線程」的這個過程當中,使用非阻塞的NIO實現,這是NIO模式與BIO模式的最主要區別(其餘區別對性能影響較小,暫時略去不提)。而這個區別,在併發量較大的情形下能夠帶來Tomcat效率的顯著提高:
目前大多數HTTP請求使用的是長鏈接(HTTP/1.1默認keep-alive爲true),而長鏈接意味着,一個TCP的socket在當前請求結束後,若是沒有新的請求到來,socket不會立馬釋放,而是等timeout後再釋放。若是使用BIO,「讀取socket並交給Worker中的線程」這個過程是阻塞的,也就意味着在socket等待下一個請求或等待釋放的過程當中,處理這個socket的工做線程會一直被佔用,沒法釋放;所以Tomcat能夠同時處理的socket數目不能超過最大線程數,性能受到了極大限制。而使用NIO,「讀取socket並交給Worker中的線程」這個過程是非阻塞的,當socket在等待下一個請求或等待釋放時,並不會佔用工做線程,所以Tomcat能夠同時處理的socket數目遠大於最大線程數,併發性能大大提升。
我的理解, 可能有誤.
如圖6-1所示, Tomcat在accept隊列中接收鏈接,
backlog
)