• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 基于Jave的Web服務工作機制

    發表于:2007-05-25來源:作者:點擊數: 標簽:
    一個WEB 服務器 也被稱為一個HTTP服務器,因為它使用HTTP協議和它的客戶進行通訊,而這些客戶通常是瀏覽器。 一個基于JAVA的WEB服務器使用了兩個重要的類:java.net.Socket和java.net.ServerSocket,并且是通過HTTP消息進行通訊的。本文開頭將討論HTTP和這兩

      一個WEB服務器也被稱為一個HTTP服務器,因為它使用HTTP協議和它的客戶進行通訊,而這些客戶通常是瀏覽器。 一個基于JAVA的WEB服務器使用了兩個重要的類:java.net.Socket和java.net.ServerSocket,并且是通過HTTP消息進行通訊的。本文開頭將討論HTTP和這兩個類,后面,將解釋一個簡單WEB服務器應用程序的工作機制。

      超文本傳輸協議 (HTTP)

      HTTP協議允許服務器和客戶機通過INTERNET接收和發送數據。它是個請求和回應協議----客戶機發送請求,服務器對請求給出回應。HTTP 使用可靠的TCP 連接,默認TCP端口是80。HTTP的第一版是HTTP/0.9,隨后被 HTTP/1.0所取代。當前最新的版本是HTTP/1.1,這個在RPC2616規范文檔中給出了定義。

      這一章節簡單講敘了HTTP 1.1, 對于你理解WEB服務器應用程序發送的消息還是足夠的。如果你很感興趣,可以參考RFC 2616文檔。

      使用HTTP,客戶端通過建立一個連接和發送一個HTTP請求來初始化事務會話,服務器聯系客戶端或者回應一個callback連接給客戶端。 它們都可以中斷連接。比如,在使用WEB瀏覽器時,你可以點擊瀏覽器上的STOP按鈕來停止文件下載進程,就有效的關閉了和這個WEB服務器的HTTP連接。

      HTTP 請求(Requests)

      一個HTTP request包含三個部分:

      方法,URL,協議/版本(Method-URI-Protocol/Version)
      請求包頭Request headers
      實體包(Entity body)
      下面給出一個HTTP請求的范例:

      POST /servlet/default.jsp HTTP/1.1
      Accept: text/plain; text/html
      Accept-Language: en-gb
      Connection: Keep-Alive
      Host: localhost
      Referer: http://localhost/ch8/SendDetails.htm
      User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
      Content-Length: 33
      Content-Type: application/x-www-form-urlencoded
      Accept-Encoding: gzip, deflate

      LastName=Franks&FirstName=Michael

      請求的第一行就是method-URI-Protocol/Version。

      POST /servlet/default.jsp HTTP/1.1

      請求的是POST方法,后面的 /servlet/default.jsp 表示一個URL地址,HTTP/1.1表示協議的版本。

      HTTP標準規范定義了一些請求方法,用來給每個HTTP請求所使用。HTTP 1.1支持7中請求方法: GET, POST, HEAD, OPTIONS, PUT, DELETE, 和 TRACE。 GET和POST 在INTERNET的應用程序中是使用最普遍的兩個方法。

      URI完整的指明了一個INTERNET資源。一個URI通常是相對于服務器的根目錄被解釋的。 因此,它總是使用符號(/)開頭。一個URL實際是一個URI類型。協議版本表示當前正在使用的HTTP協議的版本。

      請求包頭(request header)包含了一些有用的客戶機環境的信息和請求的實體(entity body)信息。比如,它可以包含瀏覽器使用的語言和實體的長度等等。每個請求包頭都被CRLF(回車換行)序列所分離。

      在先前的HTTP請求中,實體是下面簡單的一行:

      LastName=Franks&FirstName=Michael

      在一個典型的HTTP請求中,這個實體能夠很容易地變得更長。

      HTTP響應(Responses)

      和請求類似,一個HTTP響應也包含三個部分:

      協議狀態 代碼描敘(Protocol-Status code-Description)
      響應包頭(Response headers)
      實體(Entity body)
      下面是HTTP響應的一個簡單范例:

      HTTP/1.1 200 OK
      Server: Microsoft-IIS/4.0
      Date: Mon, 3 Jan 1998 13:13:33 GMT
      Content-Type: text/html
      Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT
      Content-Length: 112

      <html>
      <head>
      <title>HTTP Response Example</title></head><body>
      Welcome to Brainy Software
      </body>
      </html>

      第一行的響應包頭和上面的請求包頭很相似。 第一行告訴我們,協議是使用的HTTP1.1,響應請求已成功(200表示成功),一切已OK。

      響應包頭和請求包頭相似,也包含一些有用的信息。響應的實體是HTML那一部分的內容。包頭和實體也都是被CRLF序列分離開的。

      Socket類

      套接字(socket)是網絡連接的一個端點。它使得應用程序能夠通過網絡進行讀和寫的操作。 通過在連接上發送和接受字節流,兩個位于不同計算機的軟件程序能夠彼此相互通訊。為了發送一個消息到另一個程序,你需要知道對方機器的IP地址和socket端口號。在JAVA中,一個socket是由java.net.Socket類所表示的。

      為了創建一個套接字,你可以使用Socket類的構造函數來完成。 這些構造函數接受主機名和端口:

      public Socket(String host, int port)
      host表示遠程計算機名或者IP地址,port表示該遠程應用的端口號。比如,要在80端口連接到yahoo.com,你需要構造下面的socket:

      new Socket("yahoo.com", 80);
      一旦你成功創建了一個Socket類的實例,就可以使用它來發送和接受字節流了。 要發送字節流,必須首先調用Socket類的getOutputStream 方法來獲得一個java.io.OutputStream對象。要發送一個文本到遠程應用程序,經常要構造一個從OutputStream對象返回的java.io.PrintWriter對象。要接收連接另一端的字節流,要調用Socket類的getInputStream方法,該方法是從 java.io.InputStream返回的。

      下面的程序段創建了一個socket,和本地HTTP服務器(127.0.0.1代表本地)進行通訊,發送一個HTTP請求,然后從服務器接收一個響應。它創建了一個StringBuffer 來保存響應,并將它打印到控制臺。

      Socket socket  = new Socket("127.0.0.1", "8080");
      OutputStream os = socket.getOutputStream();
      boolean autoflush = true;
      PrintWriter out = new PrintWriter( socket.getOutputStream(), autoflush );
      BufferedReader in = new BufferedReader(
      new InputStreamReader( socket.getInputStream() ));

      // send an HTTP request to the web server
      out.println("GET /index.jsp HTTP/1.1");
      out.println("Host: localhost:8080");
      out.println("Connection: Close");
      out.println();

      // read the response
      boolean loop  = true;
      StringBuffer sb = new StringBuffer(8096);

      while (loop) {
      if ( in.ready() ) {
        int i=0;
        while (i!=-1) {
          i = in.read();
          sb.append((char) i);
        }
        loop = false;
      }
      Thread.currentThread().sleep(50);
    }

      // display the response to the out console
      System.out.println(sb.toString());
      socket.close();

      要從服務器得到一個確切的響應,你需要發送一個遵循HTTP協議規則的HTTP請求。如果你閱讀了上面的那段"超文本傳輸協議(HTTP)" ,那么你就應該能夠理解剛才上面建立socket的代碼。
      ServerSocket 類

      Socket 類表示的是客戶端的socket。無論什么時候,只要你想連接到一個遠程服務器的應用,你都要構建一個socket。如果你想執行一個服務器應用程序,比如HTTP服務或者FTP服務的程序,那么你需要使用不同的途徑。因為你的服務器必須一直是開機閑置,所以它不知道什么時候客戶機試圖來連接它。

      這個時候,需要使用java.net.ServerSocket 類。它會實現一個服務器socket。一個服務器socket會等待來自客戶端的連接。一旦它接收到一個連接請求,它就會創建一個 Socket 實例來處理和客戶端通訊的問題。

      要創建一個服務器socket,可以使用四種ServerSocket類構造方法中的一種來實現。你需要制定服務器socket監聽的IP地址和端口。 典型的,IP地址如果是127.0.0.1,意味著服務器socket將監聽本地機器。這個被監聽的IP地址被認為是一種綁定地址。server socket的另一個重要屬性是它的 backlog屬性,它是在server socket拒絕連接請求前,能夠接受的連接請求的最大隊列長度。

      ServerSocket類的構造函數之一如下:

      public ServerSocket(int port, int backLog, InetAddress bindingAddress);
      對于這個構造函數而言,綁定地址必須是java.net.InetAddress 的一個實例。一個簡單的辦法是通過調用它的靜態方法getByName來構造一個InetAddres對象。該方法來一個包含主機名的字符串參數:

      InetAddress.getByName("127.0.0.1");
      下面一行代碼構造一個ServerSocket ,它監聽本地機器的8080端口,backlog設置為1。

      new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
      一旦有了一個 ServerSocket 實例,可以通過調用accept方法來告訴它等待進來的連接請求。這個方法只有在有一個連接請求時才返回。它返回的是Socket類的實例。這個Socket對象能夠發送和接受來自客戶端應用的字節流,就是第一節所講到的socket類。實際上,accept 是本文提及的唯一一個在應用中使用的方法。

      Application應用

      我們的web服務器應用是ex01.pyrmont包的一部分,包含三個類:

      HttpServer
      Request
      Response

      這個應用的入口(靜態main方法)是HttpServer類。它創建了一個HttpServer 實例來調用它的await方法。 就象這個方法名所暗示的,await 方法在一個指定的端口等待一個HTTP請求,并處理它們,然后發送回應給客戶端。它保持等待狀態,直到收到一個shutdown命令。 (命令名await來代替wait的原因是wait是System.Object類中的一個用于線程方面的重要方法)

      應用僅僅只發送靜態資源,比如來自特定目錄的HTML和圖片文件。不支持動態包頭 (比如日期或者cookie) 。
      在下面的段落中,讓我們來看看這三個類吧。

      HttpServer 類

      HttpServer類表示一個web服務器,且在公共靜態目錄WEB_ROOT及它的子目錄中能為找到的那些靜態資源而服務。WEB_ROOT用以下方式初始化:

      public static final String WEB_ROOT =
      System.getProperty("user.dir") + File.separator + "webroot";

      這段代碼指明了一個包含靜態資源的webroot目錄,這些資源可用來測試該應用。在該目錄中也能找到servlet容器。

      要請求一個靜態資源,在瀏覽器中輸入如下地址或URL:

      http://machineName:port/staticResource
      machineName 是運行這個應用的計算機名或者IP地址。如果你的瀏覽器是在同一臺機器上,可以使用localhost作為機器名。端口是8080。staticResource是請求的文件夾名,它必須位于WEB-ROOT目錄中。

      必然,如果你使用同一個計算機來測試應用,你想向HttpServer請求發送一個index.html 文件,那么使用如下URL:

      http://localhost:8080/index.html

      想要停止服務器,可以通過發送一個shutdown命令。該命令是被HttpServer 類中的靜態SHUTDOWN_COMMAND變量所定義:

      private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
      因此,要停止服務,你可以使用命令:

      http://localhost:8080/SHUTDOWN
      現在讓我們來看看前面提到的await方法。下面一個程序清單給出了解釋。

      Listing 1.1. The HttpServer class' await method

      public void await() {
      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);
      }

      // Loop waiting for a request
      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;
        }
      }
    }

      await方法是通過創建一個ServerSocket實例而開始的。然后它進入了一個WHILE循環:

      serverSocket = new ServerSocket(
      port, 1, InetAddress.getByName("127.0.0.1"));

      ...

      // Loop waiting for a request
      while (!shutdown) {
        ...
      }

      socket = serverSocket.accept();
      在收到一個請求后,await方法從accept方法返回的socket實例中獲得java.io.InputStream 和java.io.OutputStream對象。
      input = socket.getInputStream();
      output = socket.getOutputStream();
    await于是就創建一個Request對象并調用它的 parse 方法來解析原始的HTTP請求信息。

      // create Request object and parse
      Request request = new Request(input);
      request.parse();

      接下來,await 方法創建了一個Response 對象,使用setRequest方法并調用它的sendStaticResource 方法。

      // create Response object
      Response response = new Response(output);
      response.setRequest(request);
      response.sendStaticResource();

      最后,await關閉該Socket。調用Request的getUri方法來檢查HTTP請求的URI是否是一個shutdown命令。如果是,shutdown變量被設置為true,程序退出while循環。

      // Close the socket
      socket.close();
      //check if the previous URI is a shutdown command
      shutdown = request.getUri().equals(SHUTDOWN_COMMAND);

      Request類

      Request類代表一個HTTP請求。Socket處理客戶端的通訊,將返回一個InputStream對象,通過傳遞該對象,可以構造一個Request類的實例。通過調用InputStream 對象的read方法來獲得這個HTTP請求的原始數據(raw data)。

      Request 有兩個公共方法:parse 和 getUri。parse方法解釋HTTP請求的原始數據。它不做很多事情----它能夠利用的唯一信息只是HTTP請求的URI ,這個URI是從私有方法 parseUri.得到的。parseUri 方法保存URI 到uri 變量中,然后調用公共方法getUri來返回一個HTTP請求的URI。

      為了理解parse 和 parseUri 方法是如何工作的,需要知道HTTP請求的內部結構。這個結構是在RFC2616文檔中定義的。

      一個HTTP請求包含三個部分:

      請求行(Request line)
      請求包頭(Headers)
      消息體(Message body)

      現在,我們僅僅只對HTTP請求的第一部分請求行(Request line)感興趣。一個請求行由方法標記開始,后面根請求的URI和協議版本,最后由CRLF字符結束。請求行中的元素被空格字符分開。比如,使用GET方法請求的index.html文件的請求行如下:

      GET /index.html HTTP/1.1 //這是一個請求行
      方法parse從socket的InputStream 中讀取整個字節流,該字節流是 Request 對象傳遞進來的,然后parse將這些字節流存儲在一個緩沖區里, 在緩沖區中組裝一個稱為request的StringBuffer對象。

      下面的Listing 1.2.顯示了parse方法的用法:

      Listing 1.2. The Request class' parse method

      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]);
      }

      System.out.print(request.toString());
      uri = parseUri(request.toString());
      }
      parseUri 方法從請求行那里得到URI。Listing 1.3 展示了parseUri 方法的用途。 parseUri 減縮請求中的第一個和第二個空格來獲得URI。

      Listing 1.3. The Request class' parseUri method

      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;
    }

      Response類

      Response表示一個HTTP響應。它的構造函數接受一個OutputStream對象,比如下面的:

      public Response(OutputStream output) {
      this.output = output;
      }
      Response 對象被HttpServer類的await方法構造,該方法被傳遞的參數是從socket那里得到的OutputStream對象。

      Response類有兩個公共方法: setRequest和sendStaticResource. setRequest方法傳遞一個Request對象給Response對象。Listing 1.4中的代碼顯示了這個:

      Listing 1.4. The Response class' setRequest method

      public void setRequest(Request request) {
      this.request = request;
      }
      sendStaticResource 方法用來發送一個靜態資源,比如HTML文件。Listing 1.5給出了它的實現過程:

      Listing 1.5. The Response class' sendStaticResource method

      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) {
            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();
      }
    }
      sendStaticResource 方法是非常簡單的。它首先傳遞父路徑和子路徑給File類的構造器,從而對java.io.File類進行了實例化。

      File file = new File(HttpServer.WEB_ROOT, request.getUri());
      然后它檢查文件是否存在。如果存在,sendStaticResource 方法通過傳遞File對象來構造一個java.io.FileInputStream對象。然后調用FileInputStream 的read方法,將字節流寫如到OutputStream輸出。注意這種情況下, 靜態資源的內容也被作為原始數據被發送給了瀏覽器。

    if (file.exists()) {
      fis  = new FileInputStream(file);
      int ch = fis.read(bytes, 0, BUFFER_SIZE);

      while (ch != -1) {
        output.write(bytes, 0, ch);
        ch = fis.read(bytes, 0, BUFFER_SIZE);
      }
    }

      如果這個文件不存在,sendStaticResource 方法發送一個錯誤消息給瀏覽器。

    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());

      編譯和運行應用程序

      為了編譯和運行應用,你首先需要解壓包含本文應用程序的.zip文件。你解壓的目錄成為工作目錄(working directory),它有三個子目錄: src/, classes/, 和 lib/。 要編譯應用程序需要在工作目錄輸入如下語句:

      javac -d . src/ex01/pyrmont/*.java
      這個-d 選項參數將結果寫到當前目錄,而不是src/ 目錄。

      要運行應用程序,在工作目錄中輸入如下語句:

      java ex01.pyrmont.HttpServer
      要測試你的應用程序,打開瀏覽器,在地址欄中輸入如下URL:

      http://localhost:8080/index.html
      你將可以看到瀏覽器中顯示的index.html 頁面。

      Figure 1. The output from the web server

      在控制臺(Console),你能看到如下內容:

      GET /index.html HTTP/1.1
      Accept: */*
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
      Host: localhost:8080
      Connection: Keep-Alive

      GET /images/logo.gif HTTP/1.1
      Accept: */*
      Referer: http://localhost:8080/index.html
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
      Host: localhost:8080
      Connection: Keep-Alive

      概要總結

      在本文中,你了解了一個簡單的WEB服務器的工作機制。本文附帶的應用程序源代碼只包含三個類,但并不是所有的都有用。盡管如此,它還是能被作為一種很好的學習工具為我們服務。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>