超文本傳輸協議(HTTP)是位于TCP/IP 協議的應用層,是最廣為人知的協議,也是互連網中最核心的協議之一,同樣,HTTP 也是基于 C/S 或 B/S 模型實現的。事實上,我們使用的瀏覽器如Netscape 或IE 是實現HTTP 協議中的客戶端,而一些常用的Web 服務器軟件如Apache、IIS 和iPlanet Web Server 等是實現HTTP 協議中的服務器端。Web 頁由服務端資源定位,傳輸到瀏覽器,經過瀏覽器的解釋后,被客戶所看到。
Web 的工作基于客戶機/服務器計算模型,由Web 瀏覽器(客戶機)和Web服務器(服務器)構成,兩者之間采用超文本傳送協議(HTTP)進行通信。HTTP協議是Web瀏覽器和Web服務器之間的應用層協議,是通用的、無狀態的、面向對象的協議。
一個完整的HTTP協議會話過程包括四個步驟:
◆ 連接,Web瀏覽器與Web服務器建立連接,打開一個稱為Socket(套接字)的虛擬文件,此文件的建立標志著連接建立成功;
◆ 請求,Web瀏覽器通過Socket向Web服務器提交請求。HTTP的請求一般是GET或POST命令(POST用于FORM參數的傳遞);
◆ 應答,Web瀏覽器提交請求后,通過HTTP協議傳送給Web服務器。Web服務器接到后,進行事務處理,處理結果又通過HTTP傳回給Web瀏覽器,從而在Web瀏覽器上顯示出所請求的頁面;
◆ 關閉連接,應答結束后Web瀏覽器與Web服務器必須斷開,以保證其它Web瀏覽器能夠與Web服務器建立連接。
編程思路
根據上述HTTP協議的會話過程,本實例中實現了GET請求的Web服務器程序的方法,方法如下:
通過創建ServerSocket 類對象,偵聽用戶指定的端口(為8080),等待并接受客戶機請求到端口。創建與Socket相關聯的輸入流和輸出流,然后讀取客戶機的請求信息。若請求類型是GET,則從請求信息中獲取所訪問的HTML 文件名;如果HTML 文件存在,則打開HTML 文件,把HTTP 頭信息和HTML 文件內容通過Socket 傳回給Web瀏覽器,然后關閉文件,否則發送錯誤信息給Web 瀏覽器。最后關閉與相應Web 瀏覽器連接的Socket。
用Java編寫Web服務器httpServer.java文件的源代碼如下:
一個完整的HTTP協議會話過程包括四個步驟:
◆ 連接,Web瀏覽器與Web服務器建立連接,打開一個稱為Socket(套接字)的虛擬文件,此文件的建立標志著連接建立成功;
◆ 請求,Web瀏覽器通過Socket向Web服務器提交請求。HTTP的請求一般是GET或POST命令(POST用于FORM參數的傳遞);
◆ 應答,Web瀏覽器提交請求后,通過HTTP協議傳送給Web服務器。Web服務器接到后,進行事務處理,處理結果又通過HTTP傳回給Web瀏覽器,從而在Web瀏覽器上顯示出所請求的頁面;
◆ 關閉連接,應答結束后Web瀏覽器與Web服務器必須斷開,以保證其它Web瀏覽器能夠與Web服務器建立連接。
編程思路
根據上述HTTP協議的會話過程,本實例中實現了GET請求的Web服務器程序的方法,方法如下:
通過創建ServerSocket 類對象,偵聽用戶指定的端口(為8080),等待并接受客戶機請求到端口。創建與Socket相關聯的輸入流和輸出流,然后讀取客戶機的請求信息。若請求類型是GET,則從請求信息中獲取所訪問的HTML 文件名;如果HTML 文件存在,則打開HTML 文件,把HTTP 頭信息和HTML 文件內容通過Socket 傳回給Web瀏覽器,然后關閉文件,否則發送錯誤信息給Web 瀏覽器。最后關閉與相應Web 瀏覽器連接的Socket。
用Java編寫Web服務器httpServer.java文件的源代碼如下:
//httpServer.java import java.net.*; import java.io.*; import java.util.*; import java.lang.*; public class httpServer{ public static void main(String args[]) { int port; ServerSocket server_socket; //讀取服務器端口號 try { port = Integer.parseInt(args[0]); } catch (Exception e) { port = 8080; } try { //監聽服務器端口,等待連接請求 server_socket = new ServerSocket(port); System.out.println("httpServer running on port " + server_socket.getLocalPort()); //顯示啟動信息 while(true) { Socket socket = server_socket.accept(); System.out.println("New connection accepted " + socket.getInetAddress() + ":" + socket.getPort()); //創建分線程 try { httpRequestHandler request = new httpRequestHandler(socket); Thread thread = new Thread(request); //啟動線程 thread.start(); } catch(Exception e) { System.out.println(e); } } } catch (IOException e) { System.out.println(e); } } } class httpRequestHandler implements Runnable { final static String CRLF = "\r\n"; Socket socket; InputStream input; OutputStream output; BufferedReader br; // 構造方法 public httpRequestHandler(Socket socket) throws Exception { this.socket = socket; this.input = socket.getInputStream(); this.output = socket.getOutputStream(); this.br = new BufferedReader(new InputStreamReader(socket.getInputStream())); } // 實現Runnable 接口的run()方法 public void run() { try { processRequest(); } catch(Exception e) { System.out.println(e); } } private void processRequest() throws Exception { while(true) { //讀取并顯示Web 瀏覽器提交的請求信息 String headerLine = br.readLine(); System.out.println("The client request is "+headerLine); if(headerLine.equals(CRLF) || headerLine.equals("")) break; StringTokenizer s = new StringTokenizer(headerLine); String temp = s.nextToken(); if(temp.equals("GET")) { String fileName = s.nextToken(); fileName = "." + fileName ; // 打開所請求的文件 FileInputStream fis = null ; boolean fileExists = true ; try { fis = new FileInputStream( fileName ) ; } catch ( FileNotFoundException e ) { fileExists = false ; } // 完成回應消息 String serverLine = "Server: a simple java httpServer"; String statusLine = null; String contentTypeLine = null; String entityBody = null; String contentLengthLine = "error"; if ( fileExists ) { statusLine = "HTTP/1.0 200 OK" + CRLF ; contentTypeLine = "Content-type: " + contentType( fileName ) + CRLF ; contentLengthLine = "Content-Length: " + (new Integer(fis.available())).toString() + CRLF; } else { statusLine = "HTTP/1.0 404 Not Found" + CRLF ; contentTypeLine = "text/html" ; entityBody = "<HTML>" + "<HEAD><TITLE>404 Not Found</TITLE></HEAD>" + "<BODY>404 Not Found" +"<br>usage:http://yourHostName:port/" +"fileName.html</BODY></HTML>" ; } // 發送到服務器信息 output.write(statusLine.getBytes()); output.write(serverLine.getBytes()); output.write(contentTypeLine.getBytes()); output.write(contentLengthLine.getBytes()); output.write(CRLF.getBytes()); // 發送信息內容 if (fileExists) { sendBytes(fis, output) ; fis.close(); } else { output.write(entityBody.getBytes()); } } } //關閉套接字和流 try { output.close(); br.close(); socket.close(); } catch(Exception e) {} } private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception { // 創建一個 1K buffer byte[] buffer = new byte[1024] ; int bytes = 0 ; // 將文件輸出到套接字輸出流中 while ((bytes = fis.read(buffer)) != -1 ) { os.write(buffer, 0, bytes); } } private static String contentType(String fileName) { if (fileName.endsWith(".htm") || fileName.endsWith(".html")) { return "text/html"; } return "fileName"; } } |
編程技巧說明 ◆ 主線程設計 主線程的設計就是在主線程httpServer 類中實現了服務器端口的偵聽,服務器接受一個客戶端請求之后創建一個線程實例處理請求,代碼如下:
◆ 連接處理分線程設計 在分線程httpRequestHandler 類中實現了HTTP 協議的處理,這個類實現了Runnable 接口,代碼如下:
◆ 構建processRequest()方法來處理信息的接收和發送 作為實現Runnable 接口的主要內容,在run()方法中調用processRequest()方法來處理客戶請求內容的接收和服務器返回信息的發送,代碼如下:
在processRequest()方法中得到客戶端請求后,利用一個StringTokenizer 類完成了字符串的拆分,這個類可以實現根據字符串中指定的分隔符(缺省為空格)將字符串拆分成為字串的功能。利用nextToken()方法依次得到這些字串;sendBytes()方法完成信息內容的發送,contentType()方法用于判斷文件的類型。 顯示Web頁面 顯示 Web 頁面的index.html 文件代碼如下:
運行實例 為了測試上述程序的正確性,將編譯后的httpServer.class、httpRequestHandler.class和上面的index.html文件置于網絡的某臺主機的同一目錄中。 首先運行服務器程序 java httpServer 8080,服務器程序運行后顯示端口信息“httpServer runing on port 8080”, 然后在瀏覽器的地址欄中輸入http://localhost:8080/index.html,就可以正確顯示網頁,同時在顯示“httpServer runing on port 8080 ”窗口中服務器會出現一些信息。 |
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/