how tomcat works 讀書筆記(一)----------一個簡單的web伺服器
阿新 • • 發佈:2019-02-12
http協議
若是兩個人能正常的說話交流,那麼他們間必定有一套統一的語言規則<在網路上伺服器與客戶端能交流也依賴與一套規則,它就是我們說的http規則(超文字傳輸協議Hypertext transfer protocol)。http分兩部分,一個是請求(客戶端發給伺服器),一個是回覆(伺服器發給客戶端)。
先看http請求
http回覆
下面就是http回覆的例子,除了這個圖之外,後面的部分就是大家看到的頁面的原始碼
socket
我們一般說的socket,廣義上包含java.net包下的Socket類與ServerSocekt類。另一方面有基於tcp的網路程式設計也有基於udp的網路程式設計,其中差別大家百度,這裡只談tcp。
定義性的東西大家可以檢視各種資料(個人推薦尚學堂 馬士兵老師講解的socket部分視訊,但在看socket之前建議大家先看io部分),帶大家看一段程式碼,大家應該就會知道socket程式設計的大致原理了。(程式碼來自馬士兵老師的講義)
import java.net.*; import java.io.*; public class TCPServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(6666); //在本機的TCP6666號埠上監聽 while(true) { Socket s = ss.accept(); //ServerSocket的accept為阻塞式方法,只有當它監聽到一個請求時 //它才會執行 System.out.println("a client connect!"); DataInputStream dis = new DataInputStream(s.getInputStream());//獲得客戶端向自己說的"話" //InputStream 從外部指向記憶體 System.out.println(dis.readUTF()); //按照uft-8的格式讀取內容 dis.close(); s.close(); } } } import java.net.*; import java.io.*; public class TCPClient { public static void main(String[] args) throws Exception { Socket s = new Socket("127.0.0.1", 6666); //連線127.0.0.1(本機)的tcp埠6666 OutputStream os = s.getOutputStream(); //獲得一條線路,來給伺服器"說話" DataOutputStream dos = new DataOutputStream(os);//對這個線路進行包裝 Thread.sleep(3000); //"暫停"3秒 dos.writeUTF("hello server!"); //對伺服器說 hello server! dos.flush(); dos.close(); s.close(); } }
先執行server端,再執行client端。當執行client端後,控制檯首先會列印a client connect!三秒之後會列印hello server!
模擬一個最最最基礎的tomcat
import java.net.Socket; import java.net.ServerSocket; import java.net.InetAddress; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import java.io.File; public class HttpServer { /** WEB_ROOT is the directory where our HTML and other files reside. * For this package, WEB_ROOT is the "webroot" directory under the working * directory. * The working directory is the location in the file system * from where the java command was invoked. */ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; // shutdown command private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // the shutdown command received private boolean shutdown = false; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } public void await() { ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); //這個1是什麼功能 參加 http://www.51cto.com/art/200702/40196_1.htm } catch (IOException e) { e.printStackTrace(); System.exit(1); } // Loop waiting for a request while (!shutdown) { //最開始的時候 shutdown為false 這段話會執行 Socket socket = null; InputStream input = null; OutputStream output = null; try { socket = serverSocket.accept(); //只有當有客戶端請求時 它才會執行 阻塞式方法! System.out.println(new Date()+"AAAAAAAAAAAA"); input = socket.getInputStream(); //裡面放的是客戶端對伺服器說的話 output = socket.getOutputStream();//這裡面將要放的是伺服器要對客戶端說的話 // create Request object and parse Request request = new Request(input); request.parse(); //參見request // create Response object Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); // Close the socket socket.close(); //check if the previous URI is a shutdown command shutdown = request.getUri().equals(SHUTDOWN_COMMAND); } catch (Exception e) { e.printStackTrace(); continue; } } } }
import java.io.InputStream; import java.io.IOException; public class Request { private InputStream input; private String uri; public Request(InputStream input) { this.input = input; } public void parse() { // Read a set of characters from the socket StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try { i = input.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j=0; j<i; j++) { request.append((char) buffer[j]); //將客戶端的請求加到request(StringBuffer)中 } System.out.println("request 如下"); System.out.print(request.toString()); uri = parseUri(request.toString()); System.out.println("uri 如下 "+uri); } /** *看看System.out.println("uri 如下 "+uri); 就不用解釋這個方法了 * **/ private String parseUri(String requestString) { int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 。 index1) return requestString.substring(index1 + 1, index2); } return null; } public String getUri() { return uri; } }
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
/*
HTTP Response = Status-Line
*(( general-header | response-header | entity-header ) CRLF)
CRLF
[ message-body ]
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
public class Response {
private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
System.out.println(HttpServer.WEB_ROOT+" **** "+request.getUri());
File file = new File(HttpServer.WEB_ROOT, request.getUri()); //連線使用者請求的"檔案"
if (file.exists()) {
fis = new FileInputStream(file);
int ch = fis.read(bytes, 0, BUFFER_SIZE); //把檔案裡的東西讀出來放到bytes字元數組裡
while (ch!=-1) { //把bytes數組裡的東西放到要給客戶端回覆的流裡面
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
}
else { //要是檔案不存在 不解釋
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1。File Not Found</h1。";
output.write(errorMessage.getBytes());
}
}
catch (Exception e) {
// thrown if cannot instantiate a File object
System.out.println(e.toString() );
}
finally {
if (fis!=null)
fis.close();
}
}
}
首先如果大家用的是Eclipse,那麼沒有任何問題,如果大家是用命令列的形式的話會存在一個問題,HttpServer與Response兩個類相互依賴,先編譯誰?
解決辦法 cd到三個類的目錄 然後 javac *.java
等啟動HttpServer後
在瀏覽器輸入localhost:8080/index.html
顯示如下
明白了吧 我們把index.html放到D:\尚學堂 j2ee\javase\尚學堂科技_馬士兵_J2SE_5.0_第01章_JAVA簡介_原始碼_及重要說明\java\Socket\no\WebContent 目錄下
index.html內容如下
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
我是index
</body>
</html>
再次請求 截圖如下
大家一定很奇怪,我為什麼不用火狐或這個Chrome,找個EditPlus點過來點過去。
大家試試就知道了,火狐不知道因為什麼原因,在位址列敲回車後,會發出兩次請求。結果就是報錯。
再試試http://localhost:8080/SHUTDOWN
程式退出