您好,登錄后才能下訂單哦!
Tomcat 服務(wù)器是一個免費(fèi)的開放源代碼的Web 應(yīng)用服務(wù)器,屬于輕量級應(yīng)用服務(wù)器,在中小型系統(tǒng)和并發(fā)訪問用戶不是很多的場合下被普遍使用,是開發(fā)和調(diào)試JSP 程序的首選。今天就來為大家介紹一下關(guān)于Tomcat的詳細(xì)內(nèi)容。
聲明:
1:本系列僅記錄本人讀<<深入剖析Tomcat>>此書的一些感悟,不足之處,留言指正,不勝感激。2:本系列所有代碼參照<<深入剖析Tomcat>>,不對之處,留言指正,不勝感激。
概念:傳送門:tomcat百度百科,這里說一個點(diǎn),tomcat是輕量級的javaweb服務(wù)器,用于處理servlet/jsp等動態(tài)網(wǎng)頁,雖說也可以處理靜態(tài)網(wǎng)頁,但相比apache而言還是遜色不少。有興趣的朋友可以另行了解一下 nginx, iis,apache等其他較為流行的web服務(wù)器。
使用過tomcat的朋友應(yīng)該知道,當(dāng)java web項目部署到tomcat后,在瀏覽器地址欄里輸入:http://localhost:8080/資源路徑,便可以訪問項目資源。在這一過程中,tomcat扮演調(diào)度中心的角色,接收瀏覽器發(fā)起資源請求并解析,根據(jù)解析結(jié)果分發(fā)給指定web項目處理,然后根據(jù)處理結(jié)果,對瀏覽器響應(yīng)。對此,我們來研究一下,tomcat是怎么做到的。
項目結(jié)構(gòu):
MyTomcat
接收請求(Request)
想接收瀏覽發(fā)起的請求,需要做幾手準(zhǔn)備, 1:監(jiān)聽端口(8080), 2:接收瀏覽器連接(socket連接) 3:解析HTTP請求數(shù)據(jù)。下面是代碼模擬:
第一第二步: 使用httpServer模擬tomcat調(diào)度中心
/**
* 模擬tomcat的核心類
*/public class HttpServer {
//tomcat項目絕對路徑, 所有web項目都丟在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 {
// 啟動socket服務(wù), 監(jiān)聽8080端口,
serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("啟動myTomcat服務(wù)器失?。? + e.getMessage(), e);
}
// 沒接收到關(guān)閉命令前一直監(jiān)聽
while (!shutdown) {
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
// 接收請求
socket = serverSocket.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
// 將瀏覽器發(fā)送的請求信息封裝成請求對象
Request request = new Request(in);
request.parseRequest();
// 將相應(yīng)信息封裝相應(yīng)對象
//此處簡單響應(yīng)一個靜態(tài)資源文件
Response response = new Response(out);
//模擬頁面跳轉(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封裝請求相關(guān)信息
/**
* 請求信息封裝對象
*/public class Request {
// 瀏覽器socket連接的讀流
private InputStream in;
//請求行信息信息中的uri
private String uri;
public Request(InputStream in) {
this.in = in;
}
// 解析瀏覽器發(fā)起的請求
public void parseRequest() {
// 暫時忽略文件上傳的請求,假設(shè)都字符型請求
byte[] buff = new byte[2048];
StringBuffer sb = new StringBuffer(2048);
int len = 0;
//請求內(nèi)容
try {
len = in.read(buff);
sb.append(new String(buff, 0, len));
} catch (IOException e) {
e.printStackTrace();
}
System.out.print(sb.toString());
//解析請求行中uri信息
uri = this.parseUri(sb.toString());
}
/**tomcat接收瀏覽器發(fā)起的請求是居于http協(xié)議的,請求內(nèi)容格式:*/
/**請求行:請求方式 請求uri 協(xié)議版本*/
//GET /index HTTP/1.1
/**請求頭:以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: .....
/**請求體: 請求頭回車格一行就是請求體,get方式請求體為空*/
public String parseUri(String httpContent) {
//傳入的內(nèi)容解析第一行的請求行即可:
//請求行格式: 請求方式 請求uri 協(xié)議版本 3個內(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ā)起請求:http://localhost:8080/hello/index.html HttpServer中socket通過輸入流獲取到的數(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對象處理,該對象調(diào)用parseRequest解析,獲取請求行中的uri 數(shù)據(jù), 分析該數(shù)據(jù), 得到上下文路徑,項目名,資源名。統(tǒng)稱資源路徑。
上面數(shù)據(jù)得到: hello 項目下, index.html 資源(沒有上下文路徑)
響應(yīng)請求
當(dāng)從請求信息中獲取uri后,進(jìn)而獲取到hello 項目, index.html資源, 響應(yīng)請求就可以簡單認(rèn)為根據(jù)資源路徑查找資源,如果找到,使用socket output流直接輸出資源數(shù)據(jù)即可,如果找不到,輸出404信息。
* 處理響應(yīng)請求對象
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 {
//找得到頁面是
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 {
//頁面找不到時
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)請求數(shù)據(jù)也必須遵循h(huán)ttp協(xié)議。
對于Tomcat,還有很多內(nèi)容需要研究,如果大家對此感興趣的話,不妨和我們一起,繼續(xù)探索。現(xiàn)在就關(guān)注我們,我們繼續(xù)深入探索Tomcat。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。