1. 程式人生 > >Now . Or Never ``

Now . Or Never ``

HTTP由兩部分組成:請求和響應。當你在Web瀏覽器中輸入一個URL時,瀏覽器將根據你的要求建立併發送請求,該請求包含所輸入的URL以及一些與瀏覽器本身相關的資訊。當伺服器收到這個請求時將返回一個響應,該響應包括與該請求相關的資訊以及位於指定URL(如果有的話)的資料。直到瀏覽器解析該響應並顯示出網頁(或其他資源)為止。

HTTP請求

HTTP請求的格式如下所示:

<request-line>
<headers>
<blank line>
[<request-body>]

在HTTP請求中,第一行必須是一個請求行(request line),用來說明請求型別、要訪問的資源以及使用的HTTP版本。緊接著是一個首部(header)小節,用來說明伺服器要使用的附加資訊。在首部之後是一個空行,再此之後可以新增任意的其他資料[稱之為主體(body)]。

在HTTP中,定義了多種請求型別,通常我們關心的只有GET請求和POST請求。只要在Web瀏覽器上輸入一個URL,瀏覽器就將基於該URL向伺服器傳送一個GET請求,以告訴伺服器獲取並返回什麼資源。對於www.baidu.com的GET請求如下所示:

GET / HTTP/1.1
Host: 
www.baidu.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive

請求行的第一部分說明了該請求是GET請求。該行的第二部分是一個斜槓(/),用來說明請求的是該域名的根目錄。該行的最後一部分說明使用的是HTTP 1.1版本(另一個可選項是1.0)。那麼請求發到哪裡去呢?這就是第二行的內容。

第2行是請求的第一個首部,HOST。首部HOST將指出請求的目的地。結合HOST和上一行中的斜槓(/),可以通知伺服器請求的是www.baidu.com/(HTTP 1.1才需要使用首部HOST,而原來的1.0版本則不需要使用)。第三行中包含的是首部User-Agent,伺服器端和客戶端指令碼都能夠訪問它,它是瀏覽器型別檢測邏輯的重要基礎。該資訊由你使用的瀏覽器來定義(在本例中是Firefox 1.0.1),並且在每個請求中將自動傳送。最後一行是首部Connection,通常將瀏覽器操作設定為Keep-Alive(當然也可以設定為其他值)。注意,在最後一個首部之後有一個空行。即使不存在請求主體,這個空行也是必需的。

要傳送GET請求的引數,則必須將這些額外的資訊附在URL本身的後面。其格式類似於:

URL ? name1=value1&name2=value2&..&nameN=valueN

該資訊稱之為查詢字串(query string),它將會複製在HTTP請求的請求行中,如下所示:

GET /books/?name=Professional%20Ajax HTTP/1.1
Host: 
www.baidu.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive

注意,為了將文字“Professional Ajax”作為URL的引數,需要編碼處理其內容,將空格替換成%20,這稱為URL編碼(URL encoding),常用於HTTP的許多地方(JavaScript提供了內建的函式來處理URL編碼和解碼)。“名稱—值”(name—value)對用 & 隔開。絕大部分的伺服器端技術能夠自動對請求主體進行解碼,併為這些值的訪問提供一些邏輯方式。當然,如何使用這些資料還是由伺服器決定的。

另一方面,POST請求在請求主體中為伺服器提供了一些附加的資訊。通常,當填寫一個線上表單並提交它時,這些填入的資料將以POST請求的方式傳送給伺服器。

以下就是一個典型的POST請求:

POST / HTTP/1.1
Host: 
www.baidu.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive

name=Professional%20Ajax&publisher=Wiley

從上面可以發現, POST請求和GET請求之間有一些區別。首先,請求行開始處的GET改為了POST,以表示不同的請求型別。你會發現首部Host和User-Agent仍然存在,在後面有兩個新行。其中首部Content-Type說明了請求主體的內容是如何編碼的。瀏覽器始終以application/ x-www-form- urlencoded的格式編碼來傳送資料,這是針對簡單URL編碼的MIME型別。首部Content-Length說明了請求主體的位元組數。在首部Connection後是一個空行,再後面就是請求主體。與大多數瀏覽器的POST請求一樣,這是以簡單的“名稱—值”對的形式給出的,其中name是Professional Ajax,publisher是Wiley。你可以以同樣的格式來組織URL的查詢字串引數。

下面是一些最常見的請求頭:

    Accept:瀏覽器可接受的MIME型別。
    Accept - Charset:瀏覽器可接受的字符集。
    Accept - Encoding:瀏覽器能夠進行解碼的資料編碼方式,比如gzip。Servlet能夠向支援gzip的瀏覽器返回經gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時間。
    Accept - Language:瀏覽器所希望的語言種類,當伺服器能夠提供一種以上的語言版本時要用到。
    Authorization:授權資訊,通常出現在對伺服器傳送的WWW - Authenticate頭的應答中。
    Connection:表示是否需要持久連線。如果Servlet看到這裡的值為“Keep - Alive”,或者看到請求使用的是HTTP 1.1(HTTP 1.1預設進行持久連線),它就可以利用持久連線的優點,當頁面包含多個元素時(例如Applet,圖片),顯著地減少下載所需要的時間。要實現這一點,Servlet需要在應答中傳送一個Content - Length頭,最簡單的實現方法是:先把內容寫入ByteArrayOutputStream,然後在正式寫出內容之前計算它的大小。
    Content - Length:表示請求訊息正文的長度。
    Cookie:這是最重要的請求頭資訊之一,參見後面《Cookie處理》一章中的討論。
    From:請求傳送者的email地址,由一些特殊的Web客戶程式使用,瀏覽器不會用到它。
    Host:初始URL中的主機和埠。
    If - Modified - Since:只有當所請求的內容在指定的日期之後又經過修改才返回它,否則返回304“Not Modified”應答。
    Pragma:指定“no - cache”值表示伺服器必須返回一個重新整理後的文件,即使它是代理伺服器而且已經有了頁面的本地拷貝。
    Referer:包含一個URL,使用者從該URL代表的頁面出發訪問當前請求的頁面。
    User - Agent:瀏覽器型別,如果Servlet返回的內容與瀏覽器型別有關則該值非常有用。
    UA - Pixels,UA - Color,UA - OS,UA - CPU:由某些版本的IE瀏覽器所傳送的非標準的請求頭,表示螢幕大小、顏色深度、作業系統和CPU型別。


HTTP響應

如下所示,HTTP響應的格式與請求的格式十分類似:
<status-line>
<headers>
<blank line>
[<response-body>]

正如你所見,在響應中唯一真正的區別在於第一行中用狀態資訊代替了請求資訊。狀態行(status line)通過提供一個狀態碼來說明所請求的資源情況。以下就是一個HTTP響應的例子:

HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122

<html>
<head>
<title>Wrox Homepage</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>

在本例中,狀態行給出的HTTP狀態程式碼是200,以及訊息OK。狀態行始終包含的是狀態碼和相應的簡短訊息,以避免混亂。最常用的狀態碼有:
◆200 (OK): 找到了該資源,並且一切正常。
◆304 (NOT MODIFIED): 該資源在上次請求之後沒有任何修改。這通常用於瀏覽器的快取機制。
◆401 (UNAUTHORIZED): 客戶端無權訪問該資源。這通常會使得瀏覽器要求使用者輸入使用者名稱和密碼,以登入到伺服器。
◆403 (FORBIDDEN): 客戶端未能獲得授權。這通常是在401之後輸入了不正確的使用者名稱或密碼。
◆404 (NOT FOUND): 在指定的位置不存在所申請的資源。

在狀態行之後是一些首部。通常,伺服器會返回一個名為Data的首部,用來說明響應生成的日期和時間(伺服器通常還會返回一些關於其自身的資訊,儘管並非是必需的)。接下來的兩個首部大家應該熟悉,就是與POST請求中一樣的Content-Type和Content-Length。在本例中,首部Content-Type指定了MIME型別HTML(text/html),其編碼型別是ISO-8859-1(這是針對美國英語資源的編碼標準)。響應主體所包含的就是所請求資源的HTML原始檔(儘管還可能包含純文字或其他資源型別的二進位制資料)。瀏覽器將把這些資料顯示給使用者。

注意,這裡並沒有指明針對該響應的請求型別,不過這對於伺服器並不重要。客戶端知道每種型別的請求將返回什麼型別的資料,並決定如何使用這些資料。


附錄:使用Java套接字實現一個可以處理get和post請求的小HTTP伺服器程式
/**
* SimpleHttpServer.java
*/

import java.io.*;
import java.net.*;
import java.util.StringTokenizer;

/**
* 一個簡單的用 Java Socket 編寫的 HTTP 伺服器應用, 演示了請求和應答的協議通訊內容以及
* 給客戶端返回 HTML 文字和二進位制資料檔案(一個圖片), 同時展示了 404, 200 等狀態碼.
* 首先執行這個程式,然後開啟Web瀏覽器,鍵入
http://localhost,則這個程式能夠顯示出瀏覽器傳送了那些資訊
* 並且向瀏覽器返回一個網頁和一副圖片, 並測試同瀏覽器對話.
* 當瀏覽器看到 HTML 中帶有圖片地址時, 則會發出第二次連線來請求圖片等資源.
* 這個例子可以幫您理解 Java 的 HTTP 伺服器軟體是基於 J2SE 的 Socket 等軟體編寫的概念, 並熟悉
* HTTP 協議.
* 相反的用 Telnet 連線到已有的伺服器則可以幫忙理解瀏覽器的執行過程和伺服器端的返回內容.
*
* <pre>
*       當用戶在Web瀏覽器位址列中輸入一個帶有http://字首的URL並按下Enter後,或者在Web頁面中某個以http://開頭的超連結上單擊滑鼠,HTTP事務處理的第一個階段--建立連線階段就開始了.HTTP的預設埠是80.
*    隨著連線的建立,HTTP就進入了客戶向伺服器傳送請求的階段.客戶向伺服器傳送的請求是一個有特定格式的ASCII訊息,其語法規則為:
* < Method > < URL > < HTTP Version > < >
* { <Header>:<Value> < >}*
* < >
* { Entity Body }
*    請求訊息的頂端是請求行,用於指定方法,URL和HTTP協議的版本,請求行的最後是回車換行.方法有GET,POST,HEAD,PUT,DELETE等.
* 在請求行之後是若干個報頭(Header)行.每個報頭行都是由一個報頭和一個取值構成的二元對,報頭和取值之間以":"分隔;報頭行的最後是回車換行.常見的報頭有Accept(指定MIME媒體型別),Accept_Charset(響應訊息的編碼方式),Accept_Encoding(響應訊息的字符集),User_Agent(使用者的瀏覽器資訊)等.
*    在請求訊息的報頭行之後是一個回車換行,表明請求訊息的報頭部分結束.在這個之後是請求訊息的訊息實體(Entity Body).具體的例子參看httpRequest.txt.
*     Web伺服器在收到客戶請求並作出處理之後,要向客戶傳送應答訊息.與請求訊息一樣,應答訊息的語法規則為:
* < HTTP Version> <Status Code> [<Message>]< >
* { <Header>:<Value> < > } *
* < >
* { Entity Body }
*    應答訊息的第一行為狀態行,其中包括了HTTP版本號,狀態碼和對狀態碼進行簡短解釋的訊息;狀態行的最後是回車換行.狀態碼由3位數字組成,有5類: 
* 參看:HTTP應答碼及其意義 

* 1XX 保留 
* 2XX 表示成功 
* 3XX 表示URL已經被移走 
* 4XX 表示客戶錯誤 
* 5XX 表示伺服器錯誤 
* 例如:415,表示不支援改媒體型別;503,表示伺服器不能訪問.最常見的是200,表示成功.常見的報頭有:Last_Modified(最後修改時間),Content_Type(訊息內容的MIME型別),Content_Length(內容長度)等.
*    在報頭行之後也是一個回車換行,用以表示應答訊息的報頭部分的結束,以及應答訊息實體的開始.
*    下面是一個應答訊息的例子:
* HTTP/1.0 200 OK
* Date: Moday,07-Apr-97 21:13:02 GMT
* Server:NCSA/1.1
* MIME_Version:1.0
* Content_Type:text/html
* Last_Modified:Thu Dec 5 09:28:01 1996
* Coentent_Length:3107

* <HTML><HEAD><TITLE></HTML>

* 在用Java語言實現HTTP伺服器時,首先啟動一個java.net.ServerSocket在提供服務的埠上監聽連線.向客戶返回文字時,可以用PrintWriter,但是如果返回二進位制資料,則必須使用OutputStream.write(byte[])方法,返回的應答訊息字串可以使用String.getBytes()方法轉換為位元組陣列返回,或者使用PrintStream的print()方法寫入文字,用write(byte[])方法寫入二進位制資料.

* </pre>
* @author 劉長炯
* @version 1.0 2007-07-24 Sunday
*/
public class SimpleHttpServer implements Runnable {
    /**
     * 
     */
    ServerSocket serverSocket;//伺服器Socket
    
    /**
     * 伺服器監聽埠, 預設為 80.
     */
    public static int PORT=80;//標準HTTP埠
    
    /**
     * 開始伺服器 Socket 執行緒.
     */
    public SimpleHttpServer() {
        try {
            serverSocket=new ServerSocket(PORT);
        } catch(Exception e) {
            System.out.println("無法啟動HTTP伺服器:"+e.getLocalizedMessage());
        }
        if(serverSocket==null) System.exit(1);//無法開始伺服器
        new Thread(this).start();
        System.out.println("HTTP伺服器正在執行,埠:"+PORT);
    }
    
    /**
     * 執行伺服器主執行緒, 監聽客戶端請求並返回響應.
     */
    public void run() {
        while(true) {
            try {
                Socket client=null;//客戶Socket
                client=serverSocket.accept();//客戶機(這裡是 IE 等瀏覽器)已經連線到當前伺服器
                if(client!=null) {
                    System.out.println("連線到伺服器的使用者:"+client);
                    try {
                        // 第一階段: 開啟輸入流
                        BufferedReader in=new BufferedReader(new InputStreamReader(
                                client.getInputStream()));
                        
                        System.out.println("客戶端傳送的請求資訊: ***************");
                        // 讀取第一行, 請求地址
                        String line=in.readLine();
                        System.out.println(line);
                        String resource=line.substring(line.indexOf('/'),line.lastIndexOf('/')-5);
                        //獲得請求的資源的地址
                        resource=URLDecoder.decode(resource, "UTF-8");//反編碼 URL 地址
                        String method = new StringTokenizer(line).nextElement().toString();// 獲取請求方法, GET 或者 POST

                        // 讀取所有瀏覽器傳送過來的請求引數頭部資訊
                        while( (line = in.readLine()) != null) {
                            System.out.println(line);
                            
                            if(line.equals("")) break;
                        }
                        
                        // 顯示 POST 表單提交的內容, 這個內容位於請求的主體部分
                        if("POST".equalsIgnoreCase(method)) {
                            System.out.println(in.readLine());
                        }
                        
                        System.out.println("請求資訊結束 ***************");
                        System.out.println("使用者請求的資源是:"+resource);
                        System.out.println("請求的型別是: " + method);

                        // GIF 圖片就讀取一個真實的圖片資料並返回給客戶端
                        if(resource.endsWith(".gif")) {
                            fileService("images/test.gif", client);
                            closeSocket(client);
                            continue;
                        }
                        
                        // 請求 JPG 格式就報錯 404
                        if(resource.endsWith(".jpg")) {
                                                    PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                        out.println("HTTP/1.0 404 Not found");//返回應答訊息,並結束應答
                        out.println();// 根據 HTTP 協議, 空行將結束頭資訊
                        out.close();
                        closeSocket(client);
                        continue;
                        } else {
                            // 用 writer 對客戶端 socket 輸出一段 HTML 程式碼
                            PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                            out.println("HTTP/1.0 200 OK");//返回應答訊息,並結束應答
                            out.println("Content-Type:text/html;charset=GBK");
                            out.println();// 根據 HTTP 協議, 空行將結束頭資訊

                            out.println("<h1> Hello Http Server</h1>");
                            out.println("你好, 這是一個 Java HTTP 伺服器 demo 應用.<br>");
                            out.println("您請求的路徑是: " + resource + "<br>");
                            out.println("這是一個支援虛擬路徑的圖片:<img src='abc.gif'><br>" +
                                    "<a href='abc.gif'>點選開啟abc.gif, 是個伺服器虛擬路徑的圖片檔案.</a>");
                            out.println("<br>這是個會反饋 404 錯誤的的圖片:<img src='test.jpg'><br><a href='test.jpg'>點選開啟test.jpg</a><br>");
                            out.println("<form method=post action='/'>POST 表單 <input name=username value='使用者'> <input name=submit type=submit value=submit></form>");
                            out.close();

                            closeSocket(client);
                        }
                    } catch(Exception e) {
                        System.out.println("HTTP伺服器錯誤:"+e.getLocalizedMessage());
                    }
                }
                //System.out.println(client+"連線到HTTP伺服器");//如果加入這一句,伺服器響應速度會很慢
            } catch(Exception e) {
                System.out.println("HTTP伺服器錯誤:"+e.getLocalizedMessage());
            }
        }
    }
    
    /**
     * 關閉客戶端 socket 並列印一條除錯資訊.
     * @param socket 客戶端 socket.
     */
    void closeSocket(Socket socket) {
        try {
            socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
                            System.out.println(socket + "離開了HTTP伺服器");        
    }
    
    /**
     * 讀取一個檔案的內容並返回給瀏覽器端.
     * @param fileName 檔名
     * @param socket 客戶端 socket.
     */
        void fileService(String fileName, Socket socket)
    {
            
        try
        {
            PrintStream out = new PrintStream(socket.getOutputStream(), true);
            File fileToSend = new File(fileName);
            if(fileToSend.exists() && !fileToSend.isDirectory())
            {
                out.println("HTTP/1.0 200 OK");//返回應答訊息,並結束應答
                out.println("Content-Type:application/binary");
                out.println("Content-Length:" + fileToSend.length());// 返回內容位元組數
                out.println();// 根據 HTTP 協議, 空行將結束頭資訊
                
                FileInputStream fis = new FileInputStream(fileToSend);
                byte data[] = new byte[fis.available()];
                fis.read(data);
                out.write(data);
                out.close();
                fis.close();
            }
        }
        catch(Exception e)
        {
            System.out.println("傳送檔案時出錯:" + e.getLocalizedMessage());
        }
    }
    
    /**
     * 列印用途說明.
     */
    private static void usage() {
        System.out.println("Usage: java HTTPServer <port> Default port is 80.");
    }
    
    
    /**
     * 啟動簡易 HTTP 伺服器
     * @param args 
     */
    public static void main(String[] args) {
        try {
            if(args.length != 1) {
                usage();
            } else if(args.length == 1) {
                PORT = Integer.parseInt(args[0]);
            }
        } catch (Exception ex) {
            System.err.println("Invalid port arguments. It must be a integer that greater than 0");
        }
        
        new SimpleHttpServer();
    }
    
}

</HTML>
* 
* 在用Java語言實現HTTP伺服器時,首先啟動一個java.net.ServerSocket在提供服務的埠上監聽連線.向客戶返回文字時,可以用PrintWriter,但是如果返回二進位制資料,則必須使用OutputStream.write(byte[])方法,返回的應答訊息字串可以使用String.getBytes()方法轉換為位元組陣列返回,或者使用PrintStream的print()方法寫入文字,用write(byte[])方法寫入二進位制資料.
* 
* </pre>
* @author 劉長炯
* @version 1.0 2007-07-24 Sunday
*/
public class SimpleHttpServer implements Runnable {
    /**
     * 
     */
    ServerSocket serverSocket;//伺服器Socket
    
    /**
     * 伺服器監聽埠, 預設為 80.
     */
    public static int PORT=80;//標準HTTP埠
    
    /**
     * 開始伺服器 Socket 執行緒.
     */
    public SimpleHttpServer() {
        try {
            serverSocket=new ServerSocket(PORT);
        } catch(Exception e) {
            System.out.println("無法啟動HTTP伺服器:"+e.getLocalizedMessage());
        }
        if(serverSocket==null) System.exit(1);//無法開始伺服器
        new Thread(this).start();
        System.out.println("HTTP伺服器正在執行,埠:"+PORT);
    }
    
    /**
     * 執行伺服器主執行緒, 監聽客戶端請求並返回響應.
     */
    public void run() {
        while(true) {
            try {
                Socket client=null;//客戶Socket
                client=serverSocket.accept();//客戶機(這裡是 IE 等瀏覽器)已經連線到當前伺服器
                if(client!=null) {
                    System.out.println("連線到伺服器的使用者:"+client);
                    try {
                        // 第一階段: 開啟輸入流
                        BufferedReader in=new BufferedReader(new InputStreamReader(
                                client.getInputStream()));
                        
                        System.out.println("客戶端傳送的請求資訊: ***************");
                        // 讀取第一行, 請求地址
                        String line=in.readLine();
                        System.out.println(line);
                        String resource=line.substring(line.indexOf('/'),line.lastIndexOf('/')-5);
                        //獲得請求的資源的地址
                        resource=URLDecoder.decode(resource, "UTF-8");//反編碼 URL 地址
                        String method = new StringTokenizer(line).nextElement().toString();// 獲取請求方法, GET 或者 POST

                        // 讀取所有瀏覽器傳送過來的請求引數頭部資訊
                        while( (line = in.readLine()) != null) {
                            System.out.println(line);
                            
                            if(line.equals("")) break;
                        }
                        
                        // 顯示 POST 表單提交的內容, 這個內容位於請求的主體部分
                        if("POST".equalsIgnoreCase(method)) {
                            System.out.println(in.readLine());
                        }
                        
                        System.out.println("請求資訊結束 ***************");
                        System.out.println("使用者請求的資源是:"+resource);
                        System.out.println("請求的型別是: " + method);

                        // GIF 圖片就讀取一個真實的圖片資料並返回給客戶端
                        if(resource.endsWith(".gif")) {
                            fileService("images/test.gif", client);
                            closeSocket(client);
                            continue;
                        }
                        
                        // 請求 JPG 格式就報錯 404
                        if(resource.endsWith(".jpg")) {
                                                    PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                        out.println("HTTP/1.0 404 Not found");//返回應答訊息,並結束應答
                        out.println();// 根據 HTTP 協議, 空行將結束頭資訊
                        out.close();
                        closeSocket(client);
                        continue;
                        } else {
                            // 用 writer 對客戶端 socket 輸出一段 HTML 程式碼
                            PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                            out.println("HTTP/1.0 200 OK");//返回應答訊息,並結束應答
                            out.println("Content-Type:text/html;charset=GBK");
                            out.println();// 根據 HTTP 協議, 空行將結束頭資訊

                            out.println("<h1> Hello Http Server</h1>");
                            out.println("你好, 這是一個 Java HTTP 伺服器 demo 應用.<br>");
                            out.println("您請求的路徑是: " + resource + "<br>");
                            out.println("這是一個支援虛擬路徑的圖片:<img src="abc.gif" mce_src="abc.gif"><br>" +
                                    "<a href="abc.gif" mce_href="abc.gif">點選開啟abc.gif, 是個伺服器虛擬路徑的圖片檔案.</a>");
                            out.println("<br>這是個會反饋 404 錯誤的的圖片:<img src="test.jpg" mce_src="test.jpg"><br><a href="test.jpg" mce_href="test.jpg">點選開啟test.jpg</a><br>");
                            out.println("<form method=post action='/'>POST 表單 <input name=username value='使用者'> <input name=submit type=submit value=submit></form>");
                            out.close();

                            closeSocket(client);
                        }
                    } catch(Exception e) {
                        System.out.println("HTTP伺服器錯誤:"+e.getLocalizedMessage());
                    }
                }
                //System.out.println(client+"連線到HTTP伺服器");//如果加入這一句,伺服器響應速度會很慢
            } catch(Exception e) {
                System.out.println("HTTP伺服器錯誤:"+e.getLocalizedMessage());
            }
        }
    }
    
    /**
     * 關閉客戶端 socket 並列印一條除錯資訊.
     * @param socket 客戶端 socket.
     */
    void closeSocket(Socket socket) {
        try {
            socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
                            System.out.println(socket + "離開了HTTP伺服器");        
    }
    
    /**
     * 讀取一個檔案的內容並返回給瀏覽器端.
     * @param fileName 檔名
     * @param socket 客戶端 socket.
     */
        void fileService(String fileName, Socket socket)
    {
            
        try
        {
            PrintStream out = new PrintStream(socket.getOutputStream(), true);
            File fileToSend = new File(fileName);
            if(fileToSend.exists() && !fileToSend.isDirectory())
            {
                out.println("HTTP/1.0 200 OK");//返回應答訊息,並結束應答
                out.println("Content-Type:application/binary");
                out.println("Content-Length:" + fileToSend.length());// 返回內容位元組數
                out.println();// 根據 HTTP 協議, 空行將結束頭資訊
                
                FileInputStream fis = new FileInputStream(fileToSend);
                byte data[] = new byte[fis.available()];
                fis.read(data);
                out.write(data);
                out.close();
                fis.close();
            }
        }
        catch(Exception e)
        {
            System.out.println("傳送檔案時出錯:" + e.getLocalizedMessage());
        }
    }
    
    /**
     * 列印用途說明.
     */
    private static void usage() {
        System.out.println("Usage: java HTTPServer <port> Default port is 80.");
    }
    
    
    /**
     * 啟動簡易 HTTP 伺服器
     * @param args 
     */
    public static void main(String[] args) {
        try {
            if(args.length != 1) {
                usage();
            } else if(args.length == 1) {
                PORT = Integer.parseInt(args[0]);
            }
        } catch (Exception ex) {
            System.err.println("Invalid port arguments. It must be a integer that greater than 0");
        }
        
        new SimpleHttpServer();
    }
    
}