一個簡單的Web服務器
阿新 • • 發佈:2018-04-21
row waiting rom trace server toc 獲得 except 協議
一個簡單的Web服務器
- Web服務器也稱為超文本傳輸協議(HyperText Transfer Protocol)服務器,因為它使用HTTP和客戶端(通常是Web瀏覽器)進行通信
基於JavaWeb服務器會使用到兩個重要的類:java.net.Socket類和java.net.ServerSocket類,並通過發送HTTP消息進行通信。
1.1 HTTP
- HTTP允許Web服務器和瀏覽器通過Internet發送並接受數據,是一種基於“請求--響應”的協議。
- HTTP使用可靠地TCP連接,TCP協議默認使用TCP 80端口。
- HTTP中總是有客戶端通過建立連接並發送HTTP請求來初始化一個事務的。客戶端或服務器可提前關閉連接
- 1.1.1 HTTP請求
- HTTP請求包含三部分:
- 請求方法--統一資源標識符URI--協議/版本
- 請求頭
- 實體
- 每個HTTP請求可以使用HTTP標準中指定方法中的一種。HTTP1.1支持的方法:GET,POST,HEAD,OPTIONS,PUT,DELETE,TRACE
- URI 指定Internet資源的完整路徑。URI通常為服務器根目錄的相對路徑
- 請求頭包含客戶端環境和請求實體之間的相關信息
- 請求頭和實體之間包含一個空行,該空行只有CRLF字符,CRLF告訴HTTP服務器請求實體正文從哪裏開始
- HTTP請求包含三部分:
- 1.1.2 HTTP響應
- HTTP響應包含三部分:
- 協議--狀態碼--描述
- 響應頭
- 響應實體
- HTTP響應包含三部分:
1.2 Socket類
- 套接字是網絡連接的斷電。套接字使應用程序可以從網絡中讀寫數據。
- 應用程序之間發送或接收消息,需要知道另一個應用程序中套接字的IP地址和端口號。Java中套接字由java.net.Socket表示
- 創建一個套接字:
public Socket(java.net.String host,int port)
- 創建成功Socket實例,就可以使用該實例發送或接受字節流。要發送字節流需要調用Socket類的getOutputStream方法獲取一個OutputStream對象。接收字節流使用getInputStream方法獲得輸入流InputStream對象
- ServerSocket類
- Socket表示客戶端套接字,即想要連接到遠程服務器應用程序時創建的套接字。
- 如果要實現一個服務器向客戶端發送響應,則需要另一種套接字ServerSocket。因為服務器需要時刻待命,等待客戶端發起連接。
- ServerSocket類和Socket類並不相同。服務器套接字需要等待來自客戶端的連接。當服務器套接字收到了連接請求後,會創建一個Socket實例處理與客戶端的通信。
- 要創建服務器套接字,可以使用ServerSocket類提供的4個構造函數中的任意一個:需要指明IP地址和服務器套接字偵聽的端口號。
//如果主機只有一個IP 地址, 那麽默認情況下, 服務器程序就與該IP 地址綁定. ServerSocket 的第 4 個構造方法 ServerSocket(int port, int backlog, InetAddress bingAddr) 有一個 bindAddr 參數, 它顯式指定服務器要綁定的IP 地址, 該構造方法適用於具有多個IP 地址的主機. 假定一個主機有兩個網卡, 一個網卡用於連接到 Internet, IP為 222.67.5.94, 還有一個網卡用於連接到本地局域網, IP 地址為 192.168.3.4. 如果服務器僅僅被本地局域網中的客戶訪問, 那麽可以按如下方式創建 ServerSocket: ServerSocket serverSocket = new ServerSocket(8000, 10, InetAddress.getByName("192.168.3.4"));
1.3應用程序
- Web服務器應用程序包含三個類:
- HTTPServer
- Request
- Response
- 應用程序入口點(靜態main方法)在HTTPServer類中,main方法創建一個HTTPServer實例,然後調用其await方法。該方法會在指定端口上等待HTTP請求,對其進行處理,然後發送響應信息會客戶端。
- 1.3.1 HTTPServer類
```java
package ex01.pyrmont;import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;public class HttpServer {
}/** WEB_ROOT is the directory where our HTML and other files reside. * For this package, WEB_ROOT is the "webroot" directory under the working * directory. * The working directory is the location in the file system * from where the java command was invoked. */ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; // shutdown command private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // the shutdown command received private boolean shutdown = false; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } 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; } } }
- 1.3.2 Request類
- Request類表示一個HTTP請求。可以傳遞InputStream對象,來創建Request對象。使用read方法讀取HTTP中的數據:
```java
package ex01.pyrmont;import java.io.InputStream;
import java.io.IOException;public class Request {
private InputStream input; private String uri; public Request(InputStream input) { this.input = input; } public void parse() {//解析HTTP請求中原始數據 // 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()); } 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; } public String getUri() { return uri; }
}
1.3.3 Response類
package ex01.pyrmont; import java.io.OutputStream; import java.io.IOException; import java.io.FileInputStream; import java.io.File; /* HTTP Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ] Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ public class Response { private static final int BUFFER_SIZE = 1024; Request request; OutputStream output; public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } 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(); } } }
運行結果
一個簡單的Web服務器