Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
在,我們已經(jīng)充分了解了 HTTP 和 Socket 的關(guān)系,也了解了 HTTP 報(bào)文的格式,為了讓小伙伴能夠加深對(duì)這兩個(gè)概念的理解,本文我們來(lái)看看如何利用 Socket 模擬 HTTP 請(qǐng)求。如果小伙伴們對(duì) HTTP 和 Socket 的關(guān)系、HTTP 報(bào)文格式尚不熟悉的話(huà),可以參考前面的文章 Http 和 Socket 到底是哪門(mén)子親戚?。
由于 HTTP 是基于 TCP 協(xié)議的應(yīng)用層協(xié)議,因此我們可以用更為底層的方式來(lái)訪問(wèn) HTTP 服務(wù),即直接使用 Socket 完成 HTTP 的請(qǐng)求和響應(yīng)。我們前面說(shuō)過(guò),HTTP 的任務(wù)就是完成數(shù)據(jù)的包裝, Socket 提供了網(wǎng)絡(luò)的傳輸能力,所以我們只需要按照 HTTP 報(bào)文的格式來(lái)組裝數(shù)據(jù),然后利用 Socket 將數(shù)據(jù)發(fā)送出去,就能得到回應(yīng)。
假設(shè)我現(xiàn)在有一個(gè)數(shù)據(jù)接口 http://localhost/hello,該接口每次接收一個(gè)參數(shù) name ,調(diào)用成功之后返回給用戶(hù)一個(gè) hello:name 字符串,那我們用 Socket 來(lái)實(shí)現(xiàn)這樣一個(gè) HTTP 請(qǐng)求。
首先,我們要先組裝出 HTTP 的請(qǐng)求頭,如下(如果小伙伴對(duì)下面這個(gè)請(qǐng)求頭有疑問(wèn),請(qǐng)復(fù)習(xí) Http 和 Socket 到底是哪門(mén)子親戚?一文):
POST /hello HTTP/1.1
Accept:text/html
Accept-Language:zh-cn
Host:localhost
name=張三
我這里為了簡(jiǎn)單,只添加了三個(gè)請(qǐng)求頭,然后我們通過(guò) Socket 將上面這個(gè)字符串發(fā)送出去:
Socket socket=new Socket(InetAddress.getByName("localhost"), 80);
OutputStream os=socket.getOutputStream();
String data="name=張三";
int dataLen=data.getBytes().length;
String contentType="Content-Length:" + dataLen+"\r\n";
os.write("POST /hello HTTP/1.1\r\n".getBytes());
os.write("Accept:text/html\r\n".getBytes());
os.write("Accept-Language:zh-cn\r\n".getBytes());
os.write(contentType.getBytes());
os.write("Host:localhost\r\n".getBytes());
os.write("\r\n".getBytes());
os.write(data.getBytes());
os.write("\r\n".getBytes());
os.flush();
我在 Serlvet 中接收這個(gè)請(qǐng)求并作簡(jiǎn)單處理,如下:
BufferedReader br=new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
StringBuffer sb=new StringBuffer();
String str;
while ((str=br.readLine()) !=null) {
sb.append(str).append("\r\n");
}
System.out.println("sb:"+sb.toString());
resp.setContentType("text/html;charset=utf-8");
PrintWriter out=resp.getWriter();
out.write(sb.toString());
out.flush();
out.close();
然后通過(guò) Socket 中的輸入流我就能拿到響應(yīng)結(jié)果,如下:
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuffer sb=new StringBuffer();
String str;
while ((str=br.readLine()) !=null) {
sb.append(str).append("\r\n");
}
System.out.println(sb.toString());
響應(yīng)結(jié)果如下:
HTTP/1.1 200
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Date: Sun, 03 Dec 2017 10:46:52 GMT
name=張三
這是一個(gè)簡(jiǎn)單的通過(guò) POST 請(qǐng)求下載文本的案例。接下來(lái)我們?cè)賮?lái)一個(gè) GET 請(qǐng)求下載圖片的案例,來(lái)加深對(duì) Socket 的理解。
這個(gè)實(shí)際上也不難,但是要實(shí)現(xiàn)圖片的下載需要我們首先熟悉HTTP響應(yīng)的數(shù)據(jù)格式,不熟悉的小伙伴可以閱讀 Http 和 Socket 到底是哪門(mén)子親戚?一文。
下載圖片,響應(yīng)頭是文本文件,響應(yīng)數(shù)據(jù)是二進(jìn)制文件,我們要想辦法通過(guò)空行將這兩塊數(shù)據(jù)分開(kāi),分別處理。為了解決這個(gè)問(wèn)題,我首先提供一個(gè)工具類(lèi),這個(gè)工具類(lèi)用來(lái)實(shí)現(xiàn)一行一行的解析字節(jié)流,如下:
public class BufferedLineInputStream {
private InputStream is;
public BufferedLineInputStream(InputStream is) {
this.is=is;
}
/**
* @param buf 將數(shù)據(jù)讀入到byte數(shù)組中
* @param offset 數(shù)組偏移量
* @param len 數(shù)組長(zhǎng)度
* @return 返回值表示讀取到的數(shù)據(jù)長(zhǎng)度 -1表示數(shù)據(jù)讀取完畢,0表示其他異常情況
*/
public int readLine(byte[] buf, int offset, int len) throws IOException {
if (len < 1) {
return 0;
}
//count用來(lái)統(tǒng)計(jì)已經(jīng)向數(shù)組中存儲(chǔ)了多少數(shù)據(jù)
int count=0, c;
while ((c=is.read()) !=-1) {
buf[offset++]=(byte) c;
count++;
//如果一行已經(jīng)讀完或者數(shù)組已滿(mǎn)
if (c=='\n' || count==len) {
break;
}
}
return count > 0 ? count : -1;
}
}
然后將響應(yīng)中的頭信息和圖片分別保存在不同的文件中,數(shù)據(jù)解析的核心思路就是一行一行讀取響應(yīng)數(shù)據(jù),當(dāng)遇到 \r\n 表示頭信息已經(jīng)讀取完了,要開(kāi)始讀取二進(jìn)制數(shù)據(jù)了,二進(jìn)制數(shù)據(jù)讀取到之后,將之保存成圖片即可。核心代碼如下:
fos=new FileOutputStream("E:\333.png");
pw=new PrintWriter(new OutputStreamWriter(new FileOutputStream("E:\222.txt")));
socket=new Socket(InetAddress.getByName("localhost"), 80);
OutputStream out=socket.getOutputStream();
out.write("GET /1.png HTTP/1.1\r\n".getBytes());
out.write("Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n".getBytes());
out.write("Accept-Language:zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\r\n".getBytes());
out.write("Host:localhost\r\n".getBytes());
out.write("\r\n".getBytes());
BufferedLineInputStream blis=new BufferedLineInputStream(socket.getInputStream());
int len;
byte[] buf=new byte[1024];
while ((len=blis.readLine(buf, 0, buf.length)) !=-1) {
String s=new String(buf, 0, len);
System.out.println(s);
if (s.equals("\r\n")) {//表示頭信息讀取完畢
break;
}
}
//開(kāi)始解析二進(jìn)制的圖片數(shù)據(jù)
while ((len=blis.readLine(buf, 0, buf.length)) !=-1) {
fos.write(buf, 0, len);
}
OK,Socket 模擬 HTTP 請(qǐng)求我們就先說(shuō)到這里,兩個(gè)案例,希望能夠加深小伙伴對(duì) Socket 和 HTTP 的理解。
HTTPS (HyperText Transfer Protocol Secure) 是一種通過(guò) SSL/TLS 加密保護(hù)數(shù)據(jù)傳輸安全的 HTTP 協(xié)議。HTTPS 的加密機(jī)制是保證數(shù)據(jù)在傳輸過(guò)程中不會(huì)被竊取、篡改或冒充的關(guān)鍵。
本文將詳細(xì)介紹 HTTPS 的加密過(guò)程及其工作原理。
HTTPS 的加密過(guò)程可以分為以下步驟:
下面我們將詳細(xì)介紹每個(gè)步驟的細(xì)節(jié)。
當(dāng)客戶(hù)端需要從服務(wù)器獲取數(shù)據(jù)時(shí),它會(huì)向服務(wù)器發(fā)送一個(gè) HTTPS 請(qǐng)求。這個(gè)請(qǐng)求包括請(qǐng)求的 URL、HTTP 請(qǐng)求頭和請(qǐng)求體。例如:
GET / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
當(dāng)服務(wù)器接收到 HTTPS 請(qǐng)求后,它會(huì)將公鑰證書(shū)發(fā)送給客戶(hù)端。公鑰證書(shū)中包含了服務(wù)器的公鑰、服務(wù)器的域名、證書(shū)頒發(fā)機(jī)構(gòu)、證書(shū)有效期等信息。
客戶(hù)端接收到證書(shū)后,會(huì)從中提取出服務(wù)器的公鑰。
客戶(hù)端接收到服務(wù)器的證書(shū)后,會(huì)對(duì)其進(jìn)行驗(yàn)證,以確保該證書(shū)是由可信任的證書(shū)頒發(fā)機(jī)構(gòu)頒發(fā)的,并且證書(shū)中的域名和服務(wù)器的實(shí)際域名一致。
如果證書(shū)驗(yàn)證失敗,客戶(hù)端會(huì)中斷連接。如果驗(yàn)證通過(guò),客戶(hù)端會(huì)生成一個(gè)用于會(huì)話(huà)的對(duì)稱(chēng)密鑰。
客戶(hù)端生成一個(gè)用于會(huì)話(huà)的對(duì)稱(chēng)密鑰。對(duì)稱(chēng)密鑰是一種加密方式,它使用相同的密鑰進(jìn)行加密和解密。這個(gè)密鑰只存在于客戶(hù)端和服務(wù)器之間,因此被稱(chēng)為“對(duì)稱(chēng)”。
客戶(hù)端使用服務(wù)器的公鑰對(duì)對(duì)稱(chēng)密鑰進(jìn)行加密,并將加密后的密鑰發(fā)送給服務(wù)器。在這個(gè)過(guò)程中,客戶(hù)端和服務(wù)器都知道對(duì)稱(chēng)密鑰,但是只有客戶(hù)端知道對(duì)稱(chēng)密鑰的值。
服務(wù)器使用私鑰對(duì)客戶(hù)端發(fā)送的加密密鑰進(jìn)行解密,得到對(duì)稱(chēng)密鑰。由于私鑰只在服務(wù)器端保存,因此只有服務(wù)器才能解密客戶(hù)端發(fā)送的加密密鑰,并得到對(duì)稱(chēng)密鑰的值。
服務(wù)器和客戶(hù)端使用對(duì)稱(chēng)密鑰進(jìn)行加密和解密數(shù)據(jù)傳輸。這個(gè)對(duì)稱(chēng)密鑰只存在于客戶(hù)端和服務(wù)器之間,因此對(duì)數(shù)據(jù)的加密和解密只有客戶(hù)端和服務(wù)器可以進(jìn)行。
HTTPS 的加密過(guò)程基于公鑰密碼學(xué)和對(duì)稱(chēng)密鑰密碼學(xué)。
在 HTTPS 的加密過(guò)程中,公鑰密碼學(xué)用于保護(hù)對(duì)稱(chēng)密鑰的安全傳輸,而對(duì)稱(chēng)密鑰密碼學(xué)用于加密和解密數(shù)據(jù)傳輸。
公鑰密碼學(xué)是一種密碼學(xué)技術(shù),它使用一對(duì)密鑰(公鑰和私鑰)來(lái)加密和解密數(shù)據(jù)。公鑰可以被任何人獲得并用于加密數(shù)據(jù),但是只有私鑰的擁有者才能解密數(shù)據(jù)。
在 HTTPS 的加密過(guò)程中,服務(wù)器使用公鑰加密對(duì)稱(chēng)密鑰,客戶(hù)端使用私鑰解密對(duì)稱(chēng)密鑰。這樣就可以保證對(duì)稱(chēng)密鑰在傳輸過(guò)程中不會(huì)被竊取、篡改或冒充。
對(duì)稱(chēng)密鑰密碼學(xué)是一種密碼學(xué)技術(shù),它使用相同的密鑰進(jìn)行加密和解密數(shù)據(jù)。在 HTTPS 的加密過(guò)程中,客戶(hù)端和服務(wù)器都知道對(duì)稱(chēng)密鑰的值,因此可以使用對(duì)稱(chēng)密鑰對(duì)數(shù)據(jù)進(jìn)行加密和解密。
對(duì)稱(chēng)密鑰密碼學(xué)的優(yōu)點(diǎn)是加密和解密速度快,但是對(duì)稱(chēng)密鑰的安全傳輸是一個(gè)問(wèn)題。因此,在 HTTPS 的加密過(guò)程中,公鑰密碼學(xué)用于保護(hù)對(duì)稱(chēng)密鑰的安全傳輸,保證數(shù)據(jù)在傳輸過(guò)程中不會(huì)被竊取、篡改或冒充。
HTTPS 加密是保證數(shù)據(jù)傳輸安全的關(guān)鍵。在 HTTPS 的加密過(guò)程中,公鑰密碼學(xué)用于保護(hù)對(duì)稱(chēng)密鑰的安全傳輸,對(duì)稱(chēng)密鑰密碼學(xué)用于加密和解密數(shù)據(jù)傳輸。
HTTPS 加密的過(guò)程中,客戶(hù)端和服務(wù)器之間建立了一個(gè)安全的通信通道。在這個(gè)通道中,數(shù)據(jù)被加密傳輸,確保了數(shù)據(jù)在傳輸過(guò)程中的機(jī)密性、完整性和真實(shí)性。
在今天的互聯(lián)網(wǎng)時(shí)代,數(shù)據(jù)安全越來(lái)越受到重視。HTTPS 加密作為一種保證數(shù)據(jù)傳輸安全的技術(shù),已經(jīng)成為現(xiàn)代互聯(lián)網(wǎng)通信的標(biāo)準(zhǔn)之一。通過(guò)了解 HTTPS 加密的工作原理,我們可以更好地理解互聯(lián)網(wǎng)通信的安全性,并且能夠更好地保護(hù)自己的數(shù)據(jù)安全。
Tomcat 服務(wù)器是一個(gè)免費(fèi)的開(kāi)放源代碼的Web 應(yīng)用服務(wù)器,屬于輕量級(jí)應(yīng)用服務(wù)器,在中小型系統(tǒng)和并發(fā)訪問(wèn)用戶(hù)不是很多的場(chǎng)合下被普遍使用,是開(kāi)發(fā)和調(diào)試JSP 程序的首選。對(duì)于一個(gè)初學(xué)者來(lái)說(shuō),可以這樣認(rèn)為,當(dāng)在一臺(tái)機(jī)器上配置好Apache 服務(wù)器,可利用它響應(yīng)HTML(標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言下的一個(gè)應(yīng)用)頁(yè)面的訪問(wèn)請(qǐng)求。實(shí)際上Tomcat是Apache 服務(wù)器的擴(kuò)展,但運(yùn)行時(shí)它是獨(dú)立運(yùn)行的,所以當(dāng)你運(yùn)行tomcat 時(shí),它實(shí)際上作為一個(gè)與Apache 獨(dú)立的進(jìn)程單獨(dú)運(yùn)行的。
目錄說(shuō)明bin主要存放編譯后的代碼的地方,startup.bat、shutdown.bat分別對(duì)應(yīng)windows版本的啟動(dòng)和關(guān)閉conf主要存放配置文件目錄,context.xml 存放上下文的配置信息,logging.properties存放日志配置信息,server.xml指定服務(wù)端的一些配置信息比如端口號(hào),web.xml指定一些Servlet的配置文件lib存儲(chǔ)一些依賴(lài)的jar包(tomcat也是java開(kāi)發(fā)的)Logs存放日志相關(guān)的包temp存放一些臨時(shí)文件的包webapps主要存放解壓之后的war的項(xiàng)目信息Work工作相關(guān)的信息
不同計(jì)算機(jī)上應(yīng)用之間的通信,來(lái)解決通信雙方數(shù)據(jù)傳輸?shù)膯?wèn)題。或者說(shuō)不同計(jì)算機(jī)上對(duì)應(yīng)的應(yīng)用進(jìn)程之間的通信。支持的協(xié)議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS DHCP等。(協(xié)議)
用來(lái)規(guī)定傳輸?shù)臄?shù)據(jù)格式以及加解密,格式有,JPEG、ASCll、DECOIC、加密格式等。【在四層模型里面已經(jīng)合并到了應(yīng)用層】
用來(lái)規(guī)定開(kāi)始、控制和終止一個(gè)會(huì)話(huà)。對(duì)應(yīng)主機(jī)的進(jìn)程,指本地主機(jī)與遠(yuǎn)程主機(jī)正在進(jìn)行的會(huì)話(huà)。【在四層模型里面已經(jīng)合并到了應(yīng)用層】
規(guī)定傳輸數(shù)據(jù)的協(xié)議端口號(hào),以及流控和差錯(cuò)校驗(yàn)。支持的協(xié)議有:TCP UDP等,數(shù)據(jù)包一旦離開(kāi)網(wǎng)卡即進(jìn)入網(wǎng)絡(luò)傳輸層。指定IO模型 BIO NIO AIO等
進(jìn)行邏輯地址尋址,實(shí)現(xiàn)不同網(wǎng)絡(luò)之間的路徑選擇。(路由選擇) 協(xié)議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP。
建立邏輯連接、進(jìn)行硬件地址尋址、差錯(cuò)校驗(yàn)等功能。(由底層網(wǎng)絡(luò)定義協(xié)議,ATM,F(xiàn)DDI等)將比特組合成字節(jié)進(jìn)而組合成幀,用MAC地址訪問(wèn)介質(zhì),能錯(cuò)誤發(fā)現(xiàn)但不能糾正。
建立、維護(hù)、斷開(kāi)物理連接。(由底層網(wǎng)絡(luò)定義協(xié)議,RJ45,802.3等)【在四層模型里面已經(jīng)合并到了數(shù)據(jù)鏈路層】
http請(qǐng)求處理過(guò)程
第2步是先建立連接,進(jìn)行3次握手。
因?yàn)門(mén)omcat可以處理Http請(qǐng)求,因此稱(chēng)Tomcat為Http服務(wù)器。
如果我們直接從瀏覽器發(fā)送請(qǐng)求,Tomcat直接接收請(qǐng)求,并將請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的java程序的話(huà),也可以實(shí)現(xiàn)請(qǐng)求處理,但是耦合度非常高,因此Tomcat的做法是在接收到http請(qǐng)求之后,將請(qǐng)求轉(zhuǎn)發(fā)給servlet容器,由servlet容器在進(jìn)行處理并轉(zhuǎn)發(fā)到對(duì)應(yīng)的java程序。
因此在Tomcat內(nèi)部也實(shí)現(xiàn)了Servlet規(guī)范。(Servlet接?和Servlet容器這?整套內(nèi)容叫作Servlet規(guī)范。)
因此Tomcat有兩個(gè)身份:
1、Http服務(wù)器
2、Servlet容器
步驟說(shuō)明:
1)Tomcat中Http服務(wù)器模塊會(huì)接收到原始的請(qǐng)求參數(shù)Request,封裝ServletRequest準(zhǔn)備請(qǐng)求Servlet容器
2)將請(qǐng)求轉(zhuǎn)發(fā)到Servlet容器中定位Servlet(URL和Servlet的映射關(guān)系,找到相應(yīng)的Servlet)
3)如果Servlet還未加載,利用反射機(jī)制創(chuàng)建Servlet,并調(diào)用Servlet的init初始化方法
4)獲取到具體的Servlet實(shí)例之后直接調(diào)用對(duì)應(yīng)的業(yè)務(wù)處理方法執(zhí)行業(yè)務(wù)
5)將處理好的響應(yīng)結(jié)果封裝成ServletResponse
6)Http服務(wù)器在將ServletResponse對(duì)象封裝成原生的Response相應(yīng)到瀏覽器
Tomcat中包括兩大核心組件:
連接器(Connector)組件,容器(Container)組件。(還包括其他組件)
連接器(Connector)組件主要作用:處理Socket連接,負(fù)責(zé)?絡(luò)字節(jié)流與Request和Response對(duì)象的轉(zhuǎn)化。【與客戶(hù)端交互】
容器(Container)組件主要作用:加載和管理Servlet,處理Request請(qǐng)求;
Coyote是Tomcat中連接器的組件名稱(chēng),是對(duì)應(yīng)的接口。客戶(hù)端通過(guò)Coyote與服務(wù)器建立連接、發(fā)送請(qǐng)求并接受響應(yīng)。
1)Coyote封裝了底層網(wǎng)絡(luò)通信(Socket請(qǐng)求及相應(yīng)處理)
2)Coyote使Container容器組件與具體的請(qǐng)求協(xié)議及IO操作?式完全解耦
3)Coyote將Socket輸入轉(zhuǎn)換封裝為Request對(duì)象,并進(jìn)一步封裝成ServletRequest交給Container容器進(jìn)行處理,處理完成后返回ServletResponse給Coyote,Coyote將對(duì)象轉(zhuǎn)換成Response對(duì)象將結(jié)果寫(xiě)入輸出流。
4)Coyote負(fù)責(zé)的是具體協(xié)議(應(yīng)?層)和IO(傳輸層)相關(guān)內(nèi)容。
支持的協(xié)議
應(yīng)用層應(yīng)用層描述描述HTTP/1.1大部分Web應(yīng)用采用的協(xié)議【Tomcat8.x默認(rèn)協(xié)議】AJPAJP定向包協(xié)議,實(shí)現(xiàn)對(duì)靜態(tài)資源的優(yōu)化以及集群的部署。HTTP/2HTTP2.0大幅度提升了web性能,屬于下一代的HTTP協(xié)議但是用的很少。
支持的IO
傳輸層IO模型描述NIO同步非阻塞I/O、采用javaNIO類(lèi)庫(kù)實(shí)現(xiàn)【Tomcat8默認(rèn)IO模型】NIO2異步非阻塞I/O、采用jdk7的NIO類(lèi)庫(kù)實(shí)現(xiàn)APR采用Apache可移植運(yùn)行庫(kù)實(shí)現(xiàn),是C/C++編寫(xiě)的本地庫(kù),如果使用需要單獨(dú)安裝APR庫(kù)。
Tomcat在8.0之前默認(rèn)使用的是BIO。如果使用APR性能可能達(dá)到Apache Http Server的性能。
Coyote組件其中包括EndPoint組件、Processor組件、Adapter組件。
EndPoint:EndPoint 是 Coyote 通信端點(diǎn),即通信監(jiān)聽(tīng)的接?,是具體Socket接收和發(fā)送處理器,是對(duì)傳輸層的抽象,因此EndPoint?來(lái)實(shí)現(xiàn)TCP/IP協(xié)議的。
Processor:Processor 是Coyote 協(xié)議處理接?,如果說(shuō)EndPoint是?來(lái)實(shí)現(xiàn)TCP/IP協(xié)議的,那么Processor?來(lái)實(shí)現(xiàn)HTTP協(xié)議,Processor接收來(lái)?EndPoint的Socket,讀取字節(jié)流解析成Tomcat的 Request和Response原生對(duì)象。
Adapter:Tomcat Request對(duì)象不是標(biāo)準(zhǔn)的ServletRequest,不能?Tomcat Request作為參數(shù)來(lái)調(diào)?容器。Tomcat設(shè)計(jì)者的解決?案是引?CoyoteAdapter,將參數(shù)轉(zhuǎn)換成ServlerRequest對(duì)象。
ProtocolHandler:由Endpoint 和 Processor組成,Tomcat 按照協(xié)議和I/O 提供了6個(gè)實(shí)現(xiàn)類(lèi) : AjpNioProtocol,AjpAprProtocol,AjpNio2Protocol,Http11NioProtocol,Http11Nio2Protocol,Http11AprProtocol。
本來(lái)Catalina組件只是Servlet容器的一個(gè)組件,而Tomcat是由一些列組件組成,組件可以在conf/server.xml文件中配置。Catalina在Tomcat中的地位非常的核心,因此經(jīng)常把tomcat的一個(gè)實(shí)例當(dāng)作一個(gè)Catalina實(shí)例。
整個(gè)Tomcat就相當(dāng)于一個(gè)Catalina實(shí)例,Tomcat啟動(dòng)的時(shí)候會(huì)先初始化這個(gè)實(shí)例Catalina,Catalina實(shí)例對(duì)象通過(guò)加載server.xml完成其他實(shí)例的創(chuàng)建,創(chuàng)建并管理?個(gè)Server,Server創(chuàng)建并管理多個(gè)服務(wù),每個(gè)服務(wù)?可以有多個(gè)Connector和?個(gè)Container。
對(duì)應(yīng)關(guān)系:一個(gè)Tomcat對(duì)應(yīng)一個(gè)Catalina,對(duì)應(yīng)一個(gè)Server,對(duì)應(yīng)多個(gè)Service。每一個(gè)Service實(shí)例有多個(gè)Connector和一個(gè)Container實(shí)例。
Catalina:負(fù)責(zé)解析Tomcat的配置?件(server.xml) , 以此來(lái)創(chuàng)建服務(wù)器Server組件并進(jìn)?管理
Server:Server是整個(gè)Catalina Servlet容器以及其它組件,作用負(fù)責(zé)組裝并啟動(dòng)Servlaet引擎,Tomcat連接器。Server通過(guò)實(shí)現(xiàn)Lifecycle接?,提供了?種優(yōu)雅的啟動(dòng)和關(guān)閉整個(gè)系統(tǒng)的?式。
Service:Service是Server內(nèi)部的組件,?個(gè)Server包含多個(gè)Service。它將若?個(gè)Connector組件綁定到?個(gè)Container。
Container:容器,負(fù)責(zé)處理?戶(hù)的servlet請(qǐng)求,并返回對(duì)象給web?戶(hù)的模塊。
Container組件其中包括Engine、Host、Context、Wrapper4種組件,他們之間是父子關(guān)系。Tomcat通過(guò)分層的架構(gòu),讓Servlet容器具有很好的靈活性。
Engine:表示整個(gè)Servlet容器的引擎,用來(lái)管理多個(gè)虛擬站點(diǎn),一個(gè)Service只能有一個(gè)Engine。
Host:代表一個(gè)虛擬主機(jī),或者說(shuō)一個(gè)站點(diǎn),可以配置多個(gè)虛擬主機(jī)地址,一個(gè)Engine可以有多個(gè)Host。
Context:表示一個(gè)Web應(yīng)用服務(wù)器,一個(gè)Host下可以包含多個(gè)Context。
Wrapper:表示一個(gè)Servlet, 一個(gè)Context中可以包括多個(gè)Wrapper。
思考:
去哪兒配置?->conf/server.xml中
怎么配置?
server.xml中包括Server根標(biāo)簽,Listener,GlobalNamingResources,Service
Server標(biāo)簽:主要用來(lái)創(chuàng)建一個(gè)server實(shí)體對(duì)象
Listener標(biāo)簽:定義監(jiān)聽(tīng)器
GlobalNamingResources 標(biāo)簽:定義服務(wù)器的全局JNDI(Java Naming and Directory Interface 標(biāo)準(zhǔn)的Java命名系統(tǒng)接口)資源。
Service標(biāo)簽:定義?個(gè)Service服務(wù),?個(gè)Server標(biāo)簽可以有多個(gè)Service服務(wù)實(shí)例
Server.xml整體結(jié)構(gòu)
<?xml version="1.0" encoding="UTF-8"?>
<!--
port:關(guān)閉服務(wù)器的監(jiān)聽(tīng)端?
shutdown:關(guān)閉服務(wù)器的指令字符串
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- 以?志形式輸出服務(wù)器 、操作系統(tǒng)、JVM的版本信息 -->
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<!-- 加載(服務(wù)器啟動(dòng)) 和 銷(xiāo)毀 (服務(wù)器停?) APR。 如果找不到APR庫(kù), 則會(huì)輸出?志, 并
不影響 Tomcat啟動(dòng) -->
<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<!-- 避免JRE內(nèi)存泄漏問(wèn)題 -->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<!-- 加載(服務(wù)器啟動(dòng)) 和 銷(xiāo)毀(服務(wù)器停?) 全局命名服務(wù) -->
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<!-- 在Context停?時(shí)重建 Executor 池中的線程, 以避免ThreadLocal 相關(guān)的內(nèi)存泄漏 -->
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
GlobalNamingResources 中定義了全局命名服務(wù)
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
...
</Service>
</Server>
<Server port="8005" shutdown="SHUTDOWN">
....
<Service name="Catalina">
<!--為Connector創(chuàng)建一個(gè)線程池-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!--創(chuàng)建一個(gè)監(jiān)聽(tīng)8080的連接器組件 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 創(chuàng)建連接池的連接器 -->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- 創(chuàng)建一個(gè)監(jiān)聽(tīng)8009的ajp的connector -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<!--創(chuàng)建引擎 管理 多個(gè)Host-->
<Engine name="Catalina" defaultHost="localhost">
<!--配置集群-->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!--host來(lái)管理各個(gè)Servlet-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!--servlet-->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
Service標(biāo)簽包括Executor 、Connector、Engine、Host等子標(biāo)簽。
<Executor
name="commonThreadPool" <!--指定當(dāng)前線程的名字 相當(dāng)于Id-->
namePrefix="thread-exec-" <!-- namePrefix 給線程指定前綴名字 -->
maxThreads="200" <!--指定最大線程數(shù) -->
minSpareThreads="100" <!-- 指定核心線程數(shù) -->
maxIdleTime="60000" <!-- 指定線程空閑時(shí)間 超過(guò)該時(shí)間線程被自動(dòng)銷(xiāo)毀單位是毫秒 -->
maxQueueSize="Integer.MAX_VALUE" <!-- 阻塞隊(duì)列的大小最大值 -->
prestartminSpareThreads="false" <!--確定線程池時(shí)是否啟動(dòng)核心線程-->
threadPriority="5" <!--線程中的優(yōu)先級(jí)默認(rèn)是5 取值范圍是1到10 -->
className="org.apache.catalina.core.StandardThreadExecutor" <!-- 自定義線程池類(lèi)路徑 -->
/>
Executor主要作用是來(lái)創(chuàng)建一個(gè)線程池,用于連接器Connector使用。
<Connector
port="8080" <!--監(jiān)聽(tīng)的端口號(hào)-->
protocol="HTTP/1.1"
connectionTimeout="20000" <!--連接超時(shí)時(shí)間單位毫秒-->
redirectPort="8443" />
protocol="HTTP/1.1": 當(dāng)前Connector?持的訪問(wèn)協(xié)議。 默認(rèn)為 HTTP/1.1 ,并采??動(dòng)切換機(jī)制選擇?個(gè)基于 JAVANIO 的鏈接器或者基于本地APR的鏈接器(根據(jù)本地是否含有Tomcat的本地庫(kù)判定)
redirectPort="8443":當(dāng)前Connector 不?持SSL請(qǐng)求, 接收到了?個(gè)請(qǐng)求, 并且也符合security-constraint 約束,需要SSL傳輸,Catalina?動(dòng)將請(qǐng)求重定向到指定的端?。
URIEncoding="UTF-8":?于指定編碼URI的字符編碼, Tomcat8.x版本默認(rèn)的編碼為 UTF-8 , Tomcat7.x版本默認(rèn)為ISO-8859-1。
executor="commonThreadPool":指定共享線程池的名稱(chēng),也可以通過(guò)maxThreads、minSpareThreads 等屬性配置內(nèi)部線程池。
內(nèi)部線程Demo
<Connector port="8080"
protocol="HTTP/1.1"
executor="commonThreadPool"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
connectionTimeout="20000"
compression="on" <!--是否壓縮數(shù)據(jù), on是開(kāi)啟壓縮 off關(guān)閉-->
compressionMinSize="2048" <!--壓縮的最小容量-->
disableUploadTimeout="true" <!--是否允許Servlet容器,正在執(zhí)行使用一個(gè)較長(zhǎng)的連接超時(shí)-->
redirectPort="8443"
URIEncoding="UTF-8" />
<Engine name="Catalina" defaultHost="localhost">
...
</Engine>
name屬性: ?于指定Engine 的名稱(chēng), 默認(rèn)為Catalina
defaultHost:默認(rèn)使?的虛擬主機(jī)名稱(chēng), 當(dāng)客戶(hù)端請(qǐng)求指向的主機(jī)?效時(shí), 將交由默認(rèn)的虛擬主機(jī)處
理, 默認(rèn)為localhost。
<Host name="localhost2" appBase="webapps2"
unpackWARs="true" autoDeploy="true">
<!--用來(lái)指定日志配置文件地址 valve-->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
Host標(biāo)簽屬于engine的子標(biāo)簽,主要配置虛擬主機(jī)的。默認(rèn)可以配置多個(gè),appBase假如不配置context默認(rèn)訪問(wèn)ROOT文件下的應(yīng)用。
name:用于指定虛擬主機(jī)。
appBase:對(duì)應(yīng)的配置路徑
unpackWARs:解壓war包默認(rèn)為true
autoDeploye:自動(dòng)提交
<Context docBase="/Users/demo/web_demo" path="/web3"></Context>
Context屬于Host的子標(biāo)簽,主要用來(lái)映射應(yīng)用所在的文件,比如上邊的訪問(wèn)uri是web3映射到 /Users/demo/web_demo文件夾下。
docBase:Web應(yīng)??錄或者War包的部署路徑。可以是絕對(duì)路徑,也可以是相對(duì)于Host appBase的相對(duì)路徑。
path:Web應(yīng)?的Context 路徑。如果我們Host名為localhost, 則該web應(yīng)?訪問(wèn)的根路徑為:localhost:8080/web3。
手寫(xiě)一個(gè)Tomcat名稱(chēng)叫做Minicat,需求:可以接收瀏覽器的http請(qǐng)求,并進(jìn)行請(qǐng)求處理,處理之后將結(jié)果返回給瀏覽器客戶(hù)端。
1)提供服務(wù),接收請(qǐng)求(Socket通信)
2)請(qǐng)求信息封裝成Request對(duì)象(Response對(duì)象)
3)客戶(hù)端請(qǐng)求資源,資源分為靜態(tài)資源和動(dòng)態(tài)資源
4)將資源返回給客戶(hù)端
public class Bootstrap {
//自定義端口號(hào)
public static final int port=8080;
//啟動(dòng)方法
private void start() throws IOException {
ServerSocket serverSocket=new ServerSocket(port);
System.out.println("啟動(dòng)成功,端口號(hào):" + port);
while (true) {
Socket accept=serverSocket.accept();
OutputStream outputStream=accept.getOutputStream();
String content="Hello Word !";
String retResult=HttpProtocolUtil.getHeader200(content.getBytes().length) + content;
outputStream.write(retResult.getBytes());
outputStream.close();
}
}
public static void main(String[] args) {
Bootstrap bootstrap=new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
封裝相應(yīng)頭工具類(lèi):
public class HttpProtocolUtil {
/**
* 獲取200響應(yīng)結(jié)果
*/
public static String getHeader200(int size){
return "HTTP/1.1 200 OK \n" +
"Content-Type: text/html \n" +
"Content-Length: " + size + " \n" +
"\r\n";
}
/**
* 獲取404響應(yīng)結(jié)果
*/
public static String getHeader404(){
String str404="<p1>404 Not Found</p1>";
return "HTTP/1.1 404 Not Found \n" +
"Content-Type: text/html \n" +
"Content-Length: " + str404.length() + " \n" +
"\r\n" + str404;
}
}
總結(jié):V1版本的代碼比較簡(jiǎn)單,就是簡(jiǎn)單在頁(yè)面上寫(xiě)輸入一個(gè)8080返回一個(gè)Hello Minicat但是需要注意在返回結(jié)果中必須添加上html響應(yīng)頭信息瀏覽器才能顯示
要求:能夠輸出靜態(tài)資源文件。
private void start() throws IOException {
ServerSocket serverSocket=new ServerSocket(port);
while (true){
Socket socket=serverSocket.accept();
InputStream inputStream=socket.getInputStream();
//防止網(wǎng)絡(luò)波動(dòng)讀取不到參數(shù)
int count=0;
while (count==0) {
count=inputStream.available();
}
byte[] bytes=new byte[count];
inputStream.read(bytes);
System.out.println(new String(bytes));
socket.close();
}
}
打印的參數(shù)
GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
只提取一些有用的請(qǐng)求信息,比如GET、/ 路徑信息、參數(shù)信息。來(lái)封裝成Request對(duì)象和Response對(duì)象。
public class Bootstrap {
private static final int port=8080;
public static void main(String[] args) {
Bootstrap bootstrap=new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* V2.0版本
*/
private void start() throws IOException {
ServerSocket serverSocket=new ServerSocket(port);
System.out.println("啟動(dòng)成功");
while (true){
Socket socket=serverSocket.accept();
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
//獲取到輸入輸出對(duì)象
Request request=new Request(inputStream);
Response response=new Response(outputStream);
//將結(jié)果寫(xiě)到輸出流中
response.outputHtml(request.getUrl());
//一定要把socket關(guān)閉
socket.close();
}
}
}
@Data
@NoArgsConstructor
public class Request {
//請(qǐng)求方式GET、POST
private String methodType;
//請(qǐng)求URL
private String url;
//輸入流
private InputStream inputStream;
/**
* 構(gòu)造器
*/
public Request(InputStream inputStream) throws IOException {
this.inputStream=inputStream;
parseInputParam(inputStream);
}
//解析參數(shù)
private void parseInputParam(InputStream inputStream) throws IOException {
//只提取第一行
int length=0;
while (length==0){
length=inputStream.available();
}
byte[] bytes=new byte[length];
inputStream.read(bytes);
String inputStr=new String(bytes);
//截取出來(lái) GET / HTTP/1.1
String[] split=inputStr.split("\\n");
String[] infoArr=split[0].split(" ");
this.methodType=infoArr[0];
this.url=infoArr[1];
System.out.println("=====>>method:" + methodType);
System.out.println("=====>>url:" + url);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response {
private OutputStream outputStream;
//V2版本只輸出靜態(tài)資源文件
public void outputHtml(String path) throws IOException {
//獲取到文件的絕對(duì)路徑
String absolutePath=StaticResourceUtil.getAbsolutePath(path);
File file=new File(absolutePath);
if (file.exists() && file.isFile()) {
//調(diào)用工具類(lèi)輸出文件
StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);
}else{
//404 Not Found
output(HttpProtocolUtil.getHeader404());
}
}
//輸出文件
private void output(String path) throws IOException {
outputStream.write(path.getBytes());
}
}
public class StaticResourceUtil {
/**
* 獲取到文件的絕對(duì)路徑并且替換\\為/方便linux識(shí)別
*/
public static String getAbsolutePath(String path) {
//獲取到當(dāng)前的絕對(duì)路徑
String absolutePath=StaticResourceUtil.class.getResource("/").getPath();
return (absolutePath + path).replaceAll("\\\\", "/");
}
/**
* 根據(jù)輸入流 對(duì)文件進(jìn)行輸出
*
* @param inputStream 輸入流
* @param outputStream 輸出流
*/
public static void outputStaticResource(InputStream inputStream,
OutputStream outputStream) throws IOException {
//緩沖區(qū)
int buffer=1024;
int actualOutSize=0;
//獲取需要輸出文件流的長(zhǎng)度
int outputSize=0;
while (outputSize==0){
outputSize=inputStream.available();
}
//輸出請(qǐng)求頭
outputStream.write(HttpProtocolUtil.getHeader200(outputSize).getBytes());
byte[] bytes=new byte[buffer];
while (actualOutSize < outputSize){
//如果最后不夠一個(gè)緩沖區(qū)的話(huà)需要獲取到最后的數(shù)據(jù)length
if (actualOutSize + buffer > outputSize) {
buffer=outputSize - actualOutSize;
bytes=new byte[buffer];
}
//從輸入流中讀取
inputStream.read(bytes);
//寫(xiě)出到輸出流
outputStream.write(bytes);
//刷新輸出流
outputStream.flush();
actualOutSize +=buffer;
}
}
}
總結(jié): 底層使用的是JavaSocket編程,就是對(duì)應(yīng)輸入和輸出流進(jìn)行了封裝,截取了需要的信息。
需求,能夠接收靜態(tài)和動(dòng)態(tài)資源,使用線程池接收。
基于V2版本Request和Response進(jìn)行開(kāi)發(fā)。
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.tomcat.demo.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
public interface Servlet {
//初始化
void init() throws Exception;
//銷(xiāo)毀
void destroy() throws Exception;
//執(zhí)行請(qǐng)求
void service(Request request, Response response) throws Exception;
}
public abstract class HttpServlet implements Servlet {
public abstract void doGet(Request request, Response response)throws Exception;
public abstract void doPost(Request request, Response response)throws Exception;
@Override
public void service(Request request, Response response) throws Exception {
if ("GET".equalsIgnoreCase(request.getMethodType())) {
doGet(request,response);
}else {
doPost(request,response);
}
}
}
public class MyServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) throws Exception {
doPost(request,response);
}
@Override
public void doPost(Request request, Response response) throws Exception {
String content="<H1>Hello Servlet</H1>";
String header200=HttpProtocolUtil.getHeader200(content.getBytes().length);
response.output(header200 + content);
}
@Override
public void init() throws Exception {
System.out.println("初始化方法...");
}
@Override
public void destroy() throws Exception {
System.out.println("銷(xiāo)毀方法...");
}
}
public class Bootstrap {
private static final int port=8080;
private Map<String, Servlet> servletMap=new HashMap<>();
public static void main(String[] args) {
Bootstrap bootstrap=new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 啟動(dòng)tomcat
*/
private void start() throws IOException {
ServerSocket serverSocket=new ServerSocket(port);
//解析web.xml
parseWebXml();
//創(chuàng)建一個(gè)線程池
ThreadPoolExecutor threadPool=new ThreadPoolExecutor(
20,//指定核心線程數(shù)
100,//指定最大線程數(shù)
100L,//指定存活時(shí)間
TimeUnit.MILLISECONDS,//指定時(shí)間格式
new LinkedBlockingDeque<>(1000));//設(shè)置阻塞隊(duì)列大小
while (true){
//獲取到socket
Socket socket=serverSocket.accept();
//通過(guò)線程池去執(zhí)行
threadPool.execute(new RequestProcessor(socket, servletMap));
}
}
private void parseWebXml() {
try {
InputStream resourceAsStream=this.getClass().getResourceAsStream("/web-apps/WEB-INF/web.xml");
Document document=new SAXReader().read(resourceAsStream);
Element rootElement=document.getRootElement();
Element servletElement=(Element) rootElement.selectSingleNode("servlet");
String servletName=servletElement.selectSingleNode("servlet-name").getStringValue();
String servletClass=servletElement.selectSingleNode("servlet-class").getStringValue();
Element servletMapping=(Element) rootElement.selectSingleNode("servlet-mapping[servlet-name='" + servletName + "']");
String urlPattern=servletMapping.selectSingleNode("url-pattern").getStringValue();
servletMap.put(urlPattern, (Servlet) Class.forName(servletClass).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RequestProcessor implements Runnable {
private Socket socket;
private Map<String, Servlet> servletMap;
@Override
public void run() {
try {
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
Request request=new Request(inputStream);
Response response=new Response(outputStream);
//動(dòng)態(tài)資源
if (servletMap.containsKey(request.getUrl())) {
servletMap.get(request.getUrl()).service(request, response);
}else {
//靜態(tài)資源
response.outputHtml(request.getUrl());
}
//關(guān)閉當(dāng)前socket
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
總結(jié):書(shū)寫(xiě)Mini版本的Tomcat感受一下Tomcat整體的處理思路。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。