1. 程式人生 > >基於Java實現簡單Http伺服器之一

基於Java實現簡單Http伺服器之一

        本文將詳細介紹如何基於java語言實現一個簡單的Http伺服器,文中將主要介紹三個方面的內容:1)Http協議的基本知識、2)java.net.Socket類、3)java.net.ServerSocket類,讀完本文後你可以把這個伺服器用多執行緒的技術重新編寫一個更好的伺服器。

         由於Web伺服器使用Http協議通訊的因此也把它叫做Http伺服器,Http使用可靠的TCP連線來工作,它是面向連線的通訊方式,這意味著客戶端和伺服器每次通訊都建立自己的連線,它又是無狀態的連線,當資料傳輸完畢後客戶端和伺服器端的連線立刻關閉,這樣可以節省伺服器的資源,當然如果需要傳輸大量的資料,你可以在Request的頭設定Connection=keep-alive使得可以複用這一個連線通道。在HTTP協議中非常重要的兩個概念就是:請求(Request)和(響應)這也是我在這裡要講述的如果你想了解Http更多的內容那麼請參考

http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf

         一個Http請求包括三個重要的部分:
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 ,這是非常重要的部分,你需要從中讀取客戶端資料傳輸的方式,URI以及協議和版本,在這裡分別是POST / servlet/default.jsp http/1.1,我們的簡單的伺服器的思路就是從request中得到URI後在你的伺服器上找到這個資源,比如是一個靜態的html頁面,然後把它傳送給瀏覽器。記住URI是相對於你的HTTP伺服器的根目錄的,所以以/來開頭。接下來的部分是請求頭資訊它們都是以name:value這樣的方式構成的,這裡不再多介紹了。在Header和Entity body之間有一空行叫做CRLF,這用來標記Entity body的開始的,意思是下面的是傳輸的資料了。

         HTTP響應和請求非常相似,同樣包括三個部分:
Protocol-Status code-Description
Response headers
Entity body
下面是一個具體的例子:
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

something  in html style......................

通常在J2ME聯網中我們需要判斷響應的狀態碼來決定下一步的操作,比如200代表連線成功。現在你應該清楚為什麼這麼做了吧。同樣在Header和Entity body中有一個CRLF分割。

        現在我們來看看java中的Socket類,socket其實是對程式語言的一種抽象,它提供了在網路上端對端訪問的可能,但是它並不依賴於程式語言,你完全可以使用java和c語言通過socket來進行通訊,在java中是通過java.net.Socket來實現的,當你要構建一個socket的時候,你只是需要呼叫它的構造器
public Socket(String host,int port),其中host代表目標主機的地址或名字,port代表埠,比如80。當我們建立了一個Socket的例項後我們就可以進行通訊了,如果你要基於位元組來通訊,那麼你可以通過呼叫getOutputStream()和getInputStream()來得到OutputStream和InputStream的物件,如果你是基於字元通訊的話那麼你可以用PrintWriter和BufferedReader進行二次包裝,例如PrintWriter pw = new PrintWriter(socket.getOutputStream(),true)。下面是簡單的使用socket通訊的程式碼片斷,實現了向127.0.0.1:8080傳送Http請求的功能

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

       接下來介紹與Socket類對應的ServerSocket類的使用,與Socket代表客戶端不同的是,ServerSocket是代表伺服器端的,因為它必須在某個埠不停的監視是否有客戶端連線進來。通過呼叫ServerSocket的構造器我們可以建立起監聽特定埠的Server。例如
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
這樣我們在本機的8080埠建立起來了ServerSocket。當你呼叫ServerSocket的accept()方法後,只有有連線進來的時候,這個方法才會返回一個Socket的物件,這樣你就可以使用這個例項來接受或者傳送資料了。記住當我們傳輸資料結束後要記得呼叫clos()方法來釋放資源。

       本文主要為Http伺服器的實現做鋪墊,在後面的文章內將主要講述如何實現並執行我們基於Java實現的Http伺服器。