您好,登錄后才能下訂單哦!
Tomcat最初是由Sun的軟件架構師詹姆斯·鄧肯·戴維森開發的。后來他幫助將其變為開源項目,并由Sun貢獻給Apache軟件基金會,并且成為Jakarta 項目中的一個核心項目。因此逐漸成為世界上廣泛使用的支持jsp和servlets的Web服務器。
聲明:
1:本系列僅記錄本人讀<<深入剖析Tomcat>>此書的一些感悟,不足之處,留言指正,不勝感激。2:本系列所有代碼參照<<深入剖析Tomcat>>,不對之處,留言指正,不勝感激。
概念:傳送門:tomcat百度百科,這里說一個點,tomcat是輕量級的javaweb服務器,用于處理servlet/jsp等動態網頁,雖說也可以處理靜態網頁,但相比apache而言還是遜色不少。有興趣的朋友可以另行了解一下 nginx, iis,apache等其他較為流行的web服務器。
使用過tomcat的朋友應該知道,當java web項目部署到tomcat后,在瀏覽器地址欄里輸入:http://localhost:8080/資源路徑,便可以訪問項目資源。在這一過程中,tomcat扮演調度中心的角色,接收瀏覽器發起資源請求并解析,根據解析結果分發給指定web項目處理,然后根據處理結果,對瀏覽器響應。對此,我們來研究一下,tomcat是怎么做到的。
項目結構:
MyTomcat
接收請求(Request)
想接收瀏覽發起的請求,需要做幾手準備, 1:監聽端口(8080), 2:接收瀏覽器連接(socket連接) 3:解析HTTP請求數據。下面是代碼模擬:
第一第二步: 使用httpServer模擬tomcat調度中心
/**
* 模擬tomcat的核心類
*/public class HttpServer {
//tomcat項目絕對路徑, 所有web項目都丟在webapps目錄下
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webapps";
// 模擬tomcat關閉命令
private static final String SHUTDOWN_CMD = "/SHUTDOWN";
private boolean shutdown = false;
//持續監聽端口
@SuppressWarnings("resource")
public void accept() {
ServerSocket serverSocket = null;
try {
// 啟動socket服務, 監聽8080端口,
serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("啟動myTomcat服務器失敗:" + e.getMessage(), e);
}
// 沒接收到關閉命令前一直監聽
while (!shutdown) {
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
// 接收請求
socket = serverSocket.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
// 將瀏覽器發送的請求信息封裝成請求對象
Request request = new Request(in);
request.parseRequest();
// 將相應信息封裝相應對象
//此處簡單響應一個靜態資源文件
Response response = new Response(out);
//模擬頁面跳轉
response.sendRedircet(request.getUri());
socket.close();
//如果是使用關閉命令,停止監聽退出
shutdown = request.getUri().equals(SHUTDOWN_CMD);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public static void main(String[] args) {
new HttpServer().accept();
}
}
第三步,使用HttpReqeust封裝請求相關信息
/**
* 請求信息封裝對象
*/public class Request {
// 瀏覽器socket連接的讀流
private InputStream in;
//請求行信息信息中的uri
private String uri;
public Request(InputStream in) {
this.in = in;
}
// 解析瀏覽器發起的請求
public void parseRequest() {
// 暫時忽略文件上傳的請求,假設都字符型請求
byte[] buff = new byte[2048];
StringBuffer sb = new StringBuffer(2048);
int len = 0;
//請求內容
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接收瀏覽器發起的請求是居于http協議的,請求內容格式:*/
/**請求行:請求方式 請求uri 協議版本*/
//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) {
//傳入的內容解析第一行的請求行即可:
//請求行格式: 請求方式 請求uri 協議版本 3個內容以空格隔開
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;
}
}
假設,瀏覽器發起請求:http://localhost:8080/hello/index.html HttpServer中socket通過輸入流獲取到的數據是:
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 將上述的數據交于HttpRequest對象處理,該對象調用parseRequest解析,獲取請求行中的uri 數據, 分析該數據, 得到上下文路徑,項目名,資源名。統稱資源路徑。
上面數據得到: hello 項目下, index.html 資源(沒有上下文路徑)
響應請求
當從請求信息中獲取uri后,進而獲取到hello 項目, index.html資源, 響應請求就可以簡單認為根據資源路徑查找資源,如果找到,使用socket output流直接輸出資源數據即可,如果找不到,輸出404信息。
* 處理響應請求對象
public class Response {
// 瀏覽器socket連接的寫流
private OutputStream out;
public Response(OutputStream out) {
this.out = out;
}
public OutputStream getOutputStream() {
return out;
}
//跳轉
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();
}
}
}
}
此處注意, 響應請求數據也必須遵循http協議。
這些內容只是我在<<深入剖析Tomcat>>一書中的一些理解,這些也只是Tomcat中的一部分,如果其中有什么問題,還請大家指出,當然有需要幫助的,我也會盡力幫助大家。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。