閑來無聊,研究一下Web服務器 的源程序


web服務器是如何工作的

1989年的夏天,蒂姆.博納斯-李開發了世界上第一個web服務器和web客戶機。這個瀏覽器程序是一個簡單的電話號碼查詢軟件。最初的web服務器程序就是一個利用瀏覽器和web服務器軟件之間的聯系,將存儲在硬盤上的文件傳遞給遠程的讀者。

web服務器軟件主要是提供web服務的軟件,為瀏覽器提供http數據的支持。

它無非就是把硬盤上的文件 以http數據流 的形式提供給web服務器,這就是它的基本用途。這個基本用途就是作為web服務器軟件的發明人蒂姆.博納斯-李發明web服務器的初衷。

需要傳遞的硬盤上 的文件 的格式是html格式的標記性語言的文件。web服務器軟件在接受到瀏覽器的訪問請求的時候,將直接不加任何修改的將這個html文件傳遞到遠程瀏覽器端,傳輸協議是TCP的HTTP協議。

再看下圖,深入了解他的原理。

這是一個最初的web服務器軟件 的原理圖,也是一個支持html格式文件服務 的所以web服務器 的原理圖,即使是最著名 的Apache HTTP Server也是這個原理。

所謂的靜態頁面是指本地文件直接被web服務器取得的這種web頁面。而想Asp,jsp,php這樣的所謂動態頁面是怎么個原理呢?

支持jsp的web服務器 的原理

動態頁面 的web服務器和靜態頁面的web服務器之間僅有一點的區別,就是在本地端得到html格式信息的方法不是直接從文件中讀取,而是從程序電腦生成信息中獲取而已。

那么,支持jsp的動態 的web服務器的原理又是什么養的呢?其實就是多了一個將jsp文件轉換成java文件並且編譯 的過程,然后運行那個被編譯的Class文件,從而時期得到要返回給了瀏覽器的格式信息,然后將其返回給遠端的瀏覽器。

下圖玩他的原理圖

大家估計都要已經看出來了,與返回靜態頁面的區別是,返回的信息是由過程生成的。其實,原理很簡單,無非就是讀文件發出去而已。

常用的web服務器

前面介紹的就是web服務器的 工作原理,java程序猿應該對下面的這些軟件做到非常熟悉

1.Apache HTTP Server

 Apache也許是時間最久也是最流的 http服務器軟件。快速、可靠,通過簡單的API擴展,Perl/Python解釋器可悲編譯到服務器當中,完全免費,源代碼開放。

官方的站點為http://httpd.apache.org/.

2.Tomact

是目前業界被最廣泛認可 的一個web服務器,他是Java Servlet2.2和Java Server Pages1.1技術的標准實現,是基於Apache許可證下的開發 是自由軟件,由Jakarta項目組開發,

官方站點是http://tomcat.apache.org/

研究一下web服務器的源程序

既然web服務器的原理如此簡單,那就手動自己開一個試試吧。

1.步驟一、確定用TCP作為服務傳輸協議

package yxh;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 
* @author Baron 
* @version 創建時間:2017年1月2日  
* @Dsecription 確定用TCP 作為服務傳輸協議,首先做一個main函數,建立Socket ,並用一個“死循環” 的形式
*              監聽指定端口。
 */
public class HttpServer {
    
    public static String ROOT = "./wwwroot";              ///默認root文件夾
    public static String defaultPage = "index.html";       //默認文件的文件名
    
    public static void main(String[] args) throws IOException {
          
        ServerSocket server = new ServerSocket(8000);
        while(true)
        {
            //阻塞,等待瀏覽器的連接
            Socket sk = server.accept();
            System.out.println("Accepting Connection ...\n");
            //啟動服務線程
            new HttpThread(sk).start();
        }
    }

}

 

首先做一個main函數,簡歷一個Socket並且用一個“死循環’”的形式監聽指定端口。程序如下

2.步驟2.實現一個多線程的 程序,用於處理每一個客戶的請求。程序如下。

package yxh;

import java.io.*;

import java.net.Socket;
/**
 * 
* @author Baron 
* @version 創建時間:2017年1月2日  
* @Dsecription 實現一個多線程,處理每個用戶的請求
 */
public class HttpThread extends Thread {
    
    private Socket sock;
    HttpThread(Socket socktmp){
        
        this.sock = socktmp;
    }
    public void run (){
        
        InputStream ins = null;
        OutputStream outs = null;
        try{
            ins = sock.getInputStream();
            outs= sock.getOutputStream();
            Receive rcv = new Receive(ins);
            String sURL = rcv.parse();  //用Receive 類取得瀏覽器發過來 的URL請求
            if(sURL.equals("/")){
                sURL = HttpServer.defaultPage;// 如果沒有指定文件,那么給傳過來的URL加上默認的文件名
            }
            Answer ans = new Answer(outs);
            ans.Send(sURL);//再將URL執行的文件用Answer類的send方法放回給瀏覽器
        }catch(IOException e){
            System.out.println(e.toString());
        } finally{
            //最后調用那些類的close方法釋放資源
            try{
                if(ins != null) ins.close();
                if(outs != null) outs.close();
                if(sock != null) sock.close();
            }catch(IOException e){}
        }
    }
}

 

3.步驟3.如何取得瀏覽器傳來的URL

現在實現的這個類是Receive,用這個類u取得了來自瀏覽器的字符串。程序如下。

package yxh;

import java.io.IOException;
import java.io.InputStream;
/**
 * 
* @author Baron 
* @version 創建時間:2017年1月2日  
* @Dsecription 取得瀏覽器傳過來的URL 字符串
 */
public class Receive {
   
     InputStream in  = null;
     
     public Receive(InputStream input){
         this.in = input;
     } 
     //這個方法 的目的是將URL 請求的文件返回
     public String parse(){
         StringBuffer receiveStr = new StringBuffer(2048);//這個變量就是實際從瀏覽器取得的請求數據流
         int i;
         byte[] buffer = new byte[2048];
         try{
             i = in.read(buffer);//從Socket讀出數據
         }catch(IOException e){
             i= -1;
         }
        for(int j=0;j<i;j++){
                 receiveStr.append((char)buffer[j]);//將請求到的信息循環追加到receiverStr變量中
        }
        return getUri(requestStr.toString());
     }
     private String getUri(String receiveStr){
         int index1,index2;
         index1 = receiveStr.indexOf(' ');
         if(index1 != -1){
             index2 = receiveStr.indexOf(' ', index1+1);
             if(index2>index1){
                 return receiveStr.substring(index1 +1,index2);
             }
         }
         return null;
     }
}

 

下面來解釋getUri這個方法,大家一定覺得奇怪,這個方法從兩個空格之間取得了URL的文件名,這是怎么回事?

其實,只要把receiveStr打印出來就可以了。下面來看看瀏覽器發過來的 請求是什么字符串,請求字符串如下。

requestStr:GET/index.htm HTTP/1.1

Accept:*/*

Accept-Language:zh-cn

Accept-Encoding:gzip,deflate

User-Agent:Mozilla/4.0(compatiable;MSIE 6.0 ;Windows NT 5.1;SV1;

.NET CLR 1.1.4322;MAXTHON 2.0)

Host:localhost:8000

Connection:Keep-Alive

 

那么在瀏覽器里面請求的 URL是這樣的

http://localhost:8000/index.htm

 

這時候可以發現,出現在第一個“空格”和第二個“空格”之間包圍的字符串就是“、index.htm”,這就是要取得的字符串。好了,這下可以明白,為什么取兩個空格之間的東西了吧!

那么,打印出來的 這個receiveStr其實就是按照http協議發過來的請求數據流,現在需要做的就是格局http協議將瀏覽器希望得到的內容返回出去。

4.步驟4.返回http數據流

返回的數據流的要求是:按照http 的要求返回請求文件。程序如下。

package yxh;

import java.io.*;
/**
 * 
* @author Baron 
* @version 2017年1月2日  
* @Dsecription 返回Http 數據流
 */
public class Answer {
   
    OutputStream out = null;
    public Answer(OutputStream output){
        this.out = output;
    }
    public void Send(String pagefile) throws IOException{
        //這個方法是關鍵,其目的是返回HTTP數據流
        byte[] bytes = new byte[2048];
        FileInputStream fis = null;
        try{
            //讀取指定的文件內容
            File file = new File(HttpServer.ROOT,pagefile);
            if(file.exists()){
                fis = new FileInputStream(file);
                int ch = fis.read(bytes, 0, 2048);
                @SuppressWarnings("deprecation")
                String sBody = new String(bytes,0);
                //返回 的信息是在文件的內容的前面加上Http協議的格式內容
                String sendMessage = "HTTP/1.1 200 OK\r\n"+
                "Content-Type:text/html\r\n"+
                "Content-Length:"+ch+"\r\n"+
                "\r\n"+sBody;
                //輸出文件
                out.write(sendMessage.getBytes());
            }else{
                //文件不存在的話,返回一個Http協議的格式內容
                @SuppressWarnings("unused")
                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>";
            }
        }catch(Exception e){
            System.out.println(e.toString());
        }finally {
            if(fis != null){
                fis.close();
            }
        }
    }
}

 

這回完全清楚了吧!web服務器正真返回給瀏覽器 的內容是:在http文件內容的前面加上協議內容格式信息。這個格式信息如下。

http/1.1 200 OK

Content-Type:text/html

Content-Length:[要發送的數據長度]

 

之所以在瀏覽器這邊可以看到不同的字體的顏色等信息,完全是瀏覽器自己對html標記性語言解析的結果。

有興趣的盆友如果想做一個更加強大更加完整的web服務器軟件的話,可以更深入的研究http協議。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM