您好,登錄后才能下訂單哦!
Tomcat最初是由Sun的軟件架構(gòu)師詹姆斯·鄧肯·戴維森開發(fā)的。后來(lái)他幫助將其變?yōu)殚_源項(xiàng)目,并由Sun貢獻(xiàn)給Apache軟件基金會(huì),并且成為Jakarta 項(xiàng)目中的一個(gè)核心項(xiàng)目。因此逐漸成為世界上廣泛使用的支持jsp和servlets的Web服務(wù)器。
聲明:
1:本系列僅記錄本人讀<<深入剖析Tomcat>>此書的一些感悟,不足之處,留言指正,不勝感激。2:本系列所有代碼參照<<深入剖析Tomcat>>,不對(duì)之處,留言指正,不勝感激。
概念:傳送門:tomcat百度百科,這里說(shuō)一個(gè)點(diǎn),tomcat是輕量級(jí)的javaweb服務(wù)器,用于處理servlet/jsp等動(dòng)態(tài)網(wǎng)頁(yè),雖說(shuō)也可以處理靜態(tài)網(wǎng)頁(yè),但相比apache而言還是遜色不少。有興趣的朋友可以另行了解一下 nginx, iis,apache等其他較為流行的web服務(wù)器。
使用過(guò)tomcat的朋友應(yīng)該知道,當(dāng)java web項(xiàng)目部署到tomcat后,在瀏覽器地址欄里輸入:http://localhost:8080/資源路徑,便可以訪問(wèn)項(xiàng)目資源。在這一過(guò)程中,tomcat扮演調(diào)度中心的角色,接收瀏覽器發(fā)起資源請(qǐng)求并解析,根據(jù)解析結(jié)果分發(fā)給指定web項(xiàng)目處理,然后根據(jù)處理結(jié)果,對(duì)瀏覽器響應(yīng)。對(duì)此,我們來(lái)研究一下,tomcat是怎么做到的。
項(xiàng)目結(jié)構(gòu):
MyTomcat
接收請(qǐng)求(Request)
想接收瀏覽發(fā)起的請(qǐng)求,需要做幾手準(zhǔn)備, 1:監(jiān)聽端口(8080), 2:接收瀏覽器連接(socket連接) 3:解析HTTP請(qǐng)求數(shù)據(jù)。下面是代碼模擬:
第一第二步: 使用httpServer模擬tomcat調(diào)度中心
/**
* 模擬tomcat的核心類
*/public class HttpServer {
//tomcat項(xiàng)目絕對(duì)路徑, 所有web項(xiàng)目都丟在webapps目錄下
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webapps";
// 模擬tomcat關(guān)閉命令
private static final String SHUTDOWN_CMD = "/SHUTDOWN";
private boolean shutdown = false;
//持續(xù)監(jiān)聽端口
@SuppressWarnings("resource")
public void accept() {
ServerSocket serverSocket = null;
try {
// 啟動(dòng)socket服務(wù), 監(jiān)聽8080端口,
serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("啟動(dòng)myTomcat服務(wù)器失?。? + e.getMessage(), e);
}
// 沒(méi)接收到關(guān)閉命令前一直監(jiān)聽
while (!shutdown) {
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
// 接收請(qǐng)求
socket = serverSocket.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
// 將瀏覽器發(fā)送的請(qǐng)求信息封裝成請(qǐng)求對(duì)象
Request request = new Request(in);
request.parseRequest();
// 將相應(yīng)信息封裝相應(yīng)對(duì)象
//此處簡(jiǎn)單響應(yīng)一個(gè)靜態(tài)資源文件
Response response = new Response(out);
//模擬頁(yè)面跳轉(zhuǎn)
response.sendRedircet(request.getUri());
socket.close();
//如果是使用關(guān)閉命令,停止監(jiān)聽退出
shutdown = request.getUri().equals(SHUTDOWN_CMD);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public static void main(String[] args) {
new HttpServer().accept();
}
}
第三步,使用HttpReqeust封裝請(qǐng)求相關(guān)信息
/**
* 請(qǐng)求信息封裝對(duì)象
*/public class Request {
// 瀏覽器socket連接的讀流
private InputStream in;
//請(qǐng)求行信息信息中的uri
private String uri;
public Request(InputStream in) {
this.in = in;
}
// 解析瀏覽器發(fā)起的請(qǐng)求
public void parseRequest() {
// 暫時(shí)忽略文件上傳的請(qǐng)求,假設(shè)都字符型請(qǐng)求
byte[] buff = new byte[2048];
StringBuffer sb = new StringBuffer(2048);
int len = 0;
//請(qǐng)求內(nèi)容
try {
len = in.read(buff);
sb.append(new String(buff, 0, len));
} catch (IOException e) {
e.printStackTrace();
}
System.out.print(sb.toString());
//解析請(qǐng)求行中uri信息
uri = this.parseUri(sb.toString());
}
/**tomcat接收瀏覽器發(fā)起的請(qǐng)求是居于http協(xié)議的,請(qǐng)求內(nèi)容格式:*/
/**請(qǐng)求行:請(qǐng)求方式 請(qǐng)求uri 協(xié)議版本*/
//GET /index HTTP/1.1
/**請(qǐng)求頭:以key-value形式存在*/
//Host: localhost:8080
//Connection: keep-alive
//Upgrade-Insecure-Requests: 1
//User-Agent: Mozilla/5.0 .........
//Accept: text/html,application/xhtml+xml......
//Accept-Encoding: gzip, deflate, br
//Accept-Language: zh-CN,zh;q=0.9
//Cookie: .....
/**請(qǐng)求體: 請(qǐng)求頭回車格一行就是請(qǐng)求體,get方式請(qǐng)求體為空*/
public String parseUri(String httpContent) {
//傳入的內(nèi)容解析第一行的請(qǐng)求行即可:
//請(qǐng)求行格式: 請(qǐng)求方式 請(qǐng)求uri 協(xié)議版本 3個(gè)內(nèi)容以空格隔開
int beginIndex = httpContent.indexOf(" ");
int endIndex;
if(beginIndex > -1) {
endIndex = httpContent.indexOf(" ", beginIndex + 1);
if(endIndex > beginIndex) {
return httpContent.substring(beginIndex, endIndex).trim();
}
}
return null;
}
public String getUri() {
return uri;
}
}
假設(shè),瀏覽器發(fā)起請(qǐng)求:http://localhost:8080/hello/index.html HttpServer中socket通過(guò)輸入流獲取到的數(shù)據(jù)是:
GET /hello/index.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_aa5c701f4f646931bf78b6f40b234ef5=1516445118,1516604544,1518416964,1518497222; JSESSIONID=79367FD9A55B9B442C4ED112D10FDAC5
HttpServer 將上述的數(shù)據(jù)交于HttpRequest對(duì)象處理,該對(duì)象調(diào)用parseRequest解析,獲取請(qǐng)求行中的uri 數(shù)據(jù), 分析該數(shù)據(jù), 得到上下文路徑,項(xiàng)目名,資源名。統(tǒng)稱資源路徑。
上面數(shù)據(jù)得到: hello 項(xiàng)目下, index.html 資源(沒(méi)有上下文路徑)
響應(yīng)請(qǐng)求
當(dāng)從請(qǐng)求信息中獲取uri后,進(jìn)而獲取到hello 項(xiàng)目, index.html資源, 響應(yīng)請(qǐng)求就可以簡(jiǎn)單認(rèn)為根據(jù)資源路徑查找資源,如果找到,使用socket output流直接輸出資源數(shù)據(jù)即可,如果找不到,輸出404信息。
* 處理響應(yīng)請(qǐng)求對(duì)象
public class Response {
// 瀏覽器socket連接的寫流
private OutputStream out;
public Response(OutputStream out) {
this.out = out;
}
public OutputStream getOutputStream() {
return out;
}
//跳轉(zhuǎn)
public void sendRedircet(String uri) {
File webPage = new File(HttpServer.WEB_ROOT, uri);
FileInputStream fis = null;
StringBuffer sb = new StringBuffer();
try {
//找得到頁(yè)面是
if(webPage.exists()&& webPage.isFile()) {
String respHeader = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: #{count}\r\n" +
"\r\n";
fis = new FileInputStream(webPage);
byte[] buff = new byte[2048];
int len = 0;
while( (len = fis.read(buff))!= -1) {
sb.append(new String(buff, 0, len));
}
respHeader=respHeader.replace("#{count}", sb.length()+"");
System.out.println(respHeader + sb);
out.write((respHeader + sb).getBytes());
}else {
//頁(yè)面找不到時(shí)
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h2>File Not Found</h2>";
out.write(errorMessage.getBytes());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
此處注意, 響應(yīng)請(qǐng)求數(shù)據(jù)也必須遵循h(huán)ttp協(xié)議。
這些內(nèi)容只是我在<<深入剖析Tomcat>>一書中的一些理解,這些也只是Tomcat中的一部分,如果其中有什么問(wèn)題,還請(qǐng)大家指出,當(dāng)然有需要幫助的,我也會(huì)盡力幫助大家。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。