一個簡單的web容器小例子,功能十分簡單,只能訪問靜態資源,對于新手來說還是有一定的意義。主要分三個類
1、server類:主要功能開啟socketServer,阻塞server,接收socket訪問,解析request,創建request,作出響應
public class TestServer1 { PRivate boolean shutdown = false; // web目錄webroot public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "WebRoot"; public static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; public static void main(String[] args) { TestServer1 server = new TestServer1(); server.await(); } public void await() { // 第一步、創建出serverSocket監聽本機8080端口 ServerSocket server = null; int port = 8080; try { server = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (Exception e) { e.printStackTrace(); } // 第二步、輪詢阻塞住socketServer while(!shutdown) { Socket socket = null; InputStream input = null; OutputStream output = null; try { socket = server.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); // 第三步、創建出request,解析request請求 Request req = new Request(input); req.parseRequest(); // 第四步、創建response Response response = new Response(output); response.setRequest(req); // 第五步、發送簡單靜態資源,關閉socket結束本次會話 response.sendStaticRes(); socket.close(); // 關閉指令 shutdown = req.getUri().equals(SHUTDOWN_COMMAND); } catch (IOException e) { e.printStackTrace(); } } } }
當然了上面的是最簡單的實現,也不能實現多線程,實際上的web容器肯定會創建一個線程池來接收請求
2、request 主要的工作解析input流,封裝成request
class Request { private InputStream input; private String uri; public Request(InputStream input) { this.input = input; } /** * 解析request請求內容 * GET /index.html HTTP/1.1 * Accept: text/html, application/xhtml+xml, *//* * Accept-Language: zh-CN * User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; MALCJS; rv:11.0) like Gecko * Accept-Encoding: gzip, deflate * Host: localhost:8080 * DNT: 1 * Connection: Keep-Alive * Cookie: 這個地方應該如果有cookie,如果有session還有jsessionId的 */ public void parseRequest() { // 為什么只獲取2048個字符,這是因為request請求內容的長度,出于安全或者其他方面的考慮,在瀏覽器端和服務器端都會做這么一個限制。 StringBuffer buffer = new StringBuffer(2048); byte[] bytes = new byte[2048]; int i; try { i = input.read(bytes); } catch (IOException e) { e.printStackTrace(); i=-1; } for(int k=0; k<i; k++) { buffer.append((char)bytes[k]); } System.out.println(buffer.toString()); uri = parseUri(buffer.toString()); } /** * GET /index.html HTTP/1.1 uri的位置 * @param reqStr * @return */ private String parseUri(String reqStr) { int index1, index2; index1 = reqStr.indexOf(' '); if (index1 != -1) { index2 = reqStr.indexOf(' ', index1 + 1); if (index2 > index1) return reqStr.substring(index1 + 1, index2); } return null; } public String getUri() { return uri; } }
解析成的http請求的具體內容,作為web開發人員應該要熟悉每個字段的意義,這個請求是瀏覽器本身按照http協議封裝的一個請求,能夠手寫出這個請求當然更好。
3、response 通過request的uri找到對應的資源對請求作出響應,實際的情況肯定是有靜態和動態資源例如servlet/filter等等,但是這里只是做了簡單的靜態的處理了
class Response { private OutputStream output; private Request request; public Response(OutputStream output) { this.output = output; } public void setRequest(Request req) { this.request = req; } /** * 簡單處理靜態資源 * @throws IOException */ public void sendStaticRes() throws IOException { FileInputStream fis = null; try { File staticFile = new File(TestServer1.WEB_ROOT, request.getUri()); if(staticFile.exists()) { fis = new FileInputStream(staticFile); int i = 0; byte[] buf = new byte[1024]; // 流的對拷 i = fis.read(buf, 0, 1024); while(i!=-1) { output.write(buf, 0, i); i = fis.read(buf, 0, 1024); } } else { // file not found 404 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) { e.printStackTrace(); } finally{ if(fis!=null) fis.close(); fis = null; } }}
同樣響應內容你也應該要熟悉哪些組成部分
HTTP/1.1 200 OK //響應行Date: Sat, 31 Dec 2005 23:59:59 GMTContent-Type: text/html;charset=ISO-8859-1Content-Length: 122<html><head><title>Test</title></head><body>This my page</body></html>
至此一個簡單的web容器就寫好了,能夠訪問webRoot目錄下的靜態資源。
可以通過瀏覽器訪問,也可以通過telnet端進行訪問。在cmd命令行下telnet localhost 8080,就會連接上socketServer,然后就會等待輸入,自然我們輸入對應的請求報文,回車響應內容就出現了。
以上原創文章出自老羅家的樹博客地址:http://www.companysz.com/TimBing/
新聞熱點
疑難解答