首頁  >  文章  >  Java  >  Tomcat學習筆記(一)一個簡單的Web伺服器

Tomcat學習筆記(一)一個簡單的Web伺服器

PHP中文网
PHP中文网原創
2017-07-07 18:12:481027瀏覽
內容為《深入剖析Tomcat》第一章重點,以及自己的總結,如有描述不清的,可查看原書。
一、HTTP協定:
1、定義:用於伺服器與客戶端的通訊的協議,允許web伺服器和瀏覽器透過網際網路進行發送和接收資料。是一種請求和回應協議,使用可靠的TCP協議,TCP協議的連接埠為80,是一種面向連接的協定。
2、HTTP協定請求的三個組成部分:這三部分之間用回車換行符(CRLF)隔開
     請求部分:方法(GET/POST等7種,其他的很少用,書上有介紹)[空格,該部分內容以空格隔開] 統一資源標識符URI[空格,該部分內容以空格隔開] 協定/協定版本
     URL通常都是相對伺服器的根目錄,因此以「/」開頭。
     請求頭部:請求的頭部包含了關於用戶端環境和請求的主體內容的有用資訊。例如它可能包括瀏覽器設定的語言,主體內容的長度等等。每個頭部透過一個回車換行符(CRLF)來分隔的。
     請求主體內容:對於HTTP請求格式來說,頭部和主體內容之間有一個回車換行符(CRLF)是相當重要的。 CRLF告訴HTTP伺服器主體內容是在什麼地方開始的。在一些網路程式設計書籍中,CRLF也被認為是HTTP請求的第四部分。
3、 HTTP回應也包含三個部分: 
#· 方法—統一資源標識符(URI)—協定/版本
· 回應的頭部
· 主體內容
#二、伺服器與客戶端通訊
1、伺服器與客戶端通訊需要用到兩個部分:Socket(客戶端)和ServerSocket(伺服器端)
(1)ServerSocket(java.net.ServerSocket, 伺服器端套接字),要建立一個伺服器套接字,你需要使用ServerSocket類別提供的四個建構方法中的一個。你需要指定IP位址和伺服器套接字將要進行監聽的連接埠號碼。通常,IP位址將會是127.0.0.1,也就是說,伺服器套接字將會監聽本地機器。伺服器套接字正在監聽的IP位址稱為是綁定位址。伺服器套接字的另一個重要的屬性是backlog,這是伺服器套接字開始拒絕傳入的請求之前,傳入的連線請求的最大佇列長度,四種建構方法分別為:
          ServerSocket ss = new ServerSocket();//建立一個未綁定的ServerSocket
          ServerSocket ss = new ServerSocket(int port);//建立綁定至某連接埠的ServerSocket
          ServerSocket ss = new ServerSocket(int port, int log);//建立一個綁定至某個連接埠的ServerSocket,並且設定了最大佇列長度。
          ServerSocket ss = new ServerSocket(int port, int log, InetAddress address);//建立一個綁定到某個位址、某連接埠的ServerSocket,並且設定了最大佇列長度。
          對於第四個建構方法,綁定位址必須是InetAddress的一個實例,一種建構InetAddress物件的簡單的方法是呼叫它的靜態方法getByName,傳入一個包含主機名稱的字串,就像下面的代碼一樣。
               InetAddress.getByName("127.0.0.1");
      建立ServerSocket的常用方法:  
               new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
      程式碼建構了一個監聽本機8080埠的ServletSocket,它的佇列長度為1. 
      伺服器建立後就保持等待狀態(TCP協議,可靠的傳輸協議,是同步協議,即沒有回應返回就一直等待)
(2)Socket(java.net.Socket類,客戶端套接字):需要知道要存取的伺服器端的IP/主機名稱和連接埠號,即可向伺服器端發送請求。可用Socket的眾多構造方法中的一個來建立Socket         
       new Socket ("yahoo.com", 80);
     一旦你成功創建了一個Socket類別的實例,你可以使用它來傳送和接受位元組流。要傳送位元組流,你首先必須呼叫Socket類別的getOutputStream方法來取得一個java.io.OutputStream物件。要傳送文字到一個遠端應用,你經常要從傳回的OutputStream物件建構一個java.io.PrintWriter物件。要從連接的另一端接受位元組流,你可以呼叫Socket類別的getInputStream方法用來傳回一個java.io.InputStream物件。 
(3)伺服器端透過accept()方法接收客戶端的連接請求,並與客戶端建立連接,同時傳回一個Socket
          Socket s =  ss. accept();
(4)透過Socket可以獲得一個輸入流和一個輸出流, 輸入流用於讀取客戶端請求數據,輸出流用於向客戶端返回回應資訊。
例如:InputStream input =  s.getInputStream();
          OutputStream output = s.getOutputStream();
三、簡單的Web伺服器通訊範例(建議拷貝到MyEclipse來看並執行)
1、伺服器類別
package com.socket.httpservertest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
     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() {
     //   System.out.println(System.getProperty("user.dir"));
           ServerSocket serverSocket = null;
           int port = 8080;
           try {
                serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
           } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
           }
           while (!shutdown) {
                Socket socket = null;
                InputStream input = null;
                OutputStream output = null;
                try {
                     //接收了客户端发来的请求,否则一致是等待状态
                     socket = serverSocket.accept();
                     input = socket.getInputStream();
                     output = socket.getOutputStream();
                     // create Request object and parse
                     Request request = new Request(input);
                     request.parse(); //从请求中读取内容
                     // 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;
                }
           }
    }
}
2、請求類別
package com.socket.httpservertest;
import java.io.IOException;
import java.io.InputStream;
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); //将从输入流取2048长度的内容存到buffer字节数组中,如果内容不到2048,数组其他空间剩余空着
           } catch (IOException e) {
                e.printStackTrace();
                i = -1;
           }
           
           for (int j=0; j<i; j++)
           {
                request.append((char) buffer[j]);
           }
           System.out.print(request.toString());
           uri = parseUri(request.toString());
     }
     
     private String parseUri(String requestString) {
           int index1, index2;
           index1 = requestString.indexOf(' ');
           /*
            * http请求行的结构:方法 统一资源标识符(URI) 协议/版本(它们之间以空格分隔)
            * 例如:POST //examples/default.jsp HTTP/1.1
            */
           if (index1 != -1) {// index1 == -1表示没找到
                     index2 = requestString.indexOf(' ', index1 + 1);//从index+1位置开始寻找‘ ’
                     if (index2 > index1)
                     return requestString.substring(index1 + 1, index2);
                }
           return null;
     }
     
     public String getUri()
     {
           return uri;
     }
}
3、回應類別
package com.socket.httpservertest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
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 {
                File file = new File(HttpServer.WEB_ROOT, request.getUri());
                if (file.exists()) {
                     fis = new FileInputStream(file);
                     int ch = fis.read(bytes, 0, BUFFER_SIZE);
                     while (ch!=-1) { //ch==-1表示读到末尾了
                           output.write(bytes, 0, ch); //写出到浏览器
                           ch = fis.read(bytes, 0, BUFFER_SIZE);//再读会接上一次读的位置往下读,如果读到末尾就会返回-1,终止输出
                     }
                } 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();
           }
     }
}
4、範例程式碼功能描述:瀏覽器輸入http請求,例如http://localhost:8080/MyHtml.html;伺服器接收請求後用輸入流讀取請求內容取得檔案位置,再用檔案輸入流讀取文件中的內容;最後給瀏覽器客戶端回傳回應,把html檔案中的內容顯示在瀏覽器上,如圖:
 
5、範例程式碼跨職能流程圖:

 

 
 
#

以上是Tomcat學習筆記(一)一個簡單的Web伺服器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn