Web服務(wù)器也稱為超文本傳輸協(xié)議(HTTP)服務(wù)器鞠苟,因為它使用HTTP來跟客戶端進(jìn)行通信的。既然說到通信那就離不了Java里的兩個重要的類java.net.Socket和java.net.ServerSocket迫悠。這里瀏覽器一方可以認(rèn)為是一個客戶端,接收HTTP請求的一方可以認(rèn)為是服務(wù)端安寺。在這之前我們先說一下HTTP協(xié)議凡壤。
HTTP協(xié)議允許web服務(wù)器和瀏覽器通過網(wǎng)絡(luò)來進(jìn)行發(fā)送和接收數(shù)據(jù)。它是一種請求和響應(yīng)協(xié)議橙喘∈蓖遥客戶端發(fā)送一個請求,服務(wù)端響應(yīng)這個請求厅瞎。HTTP請求由三部分組成饰潜,分別是:請求行、消息報頭(請求頭)和簸、請求正文(一般是POST請求)彭雾。注意:請求頭和請求正文中間有一個空行。一個請求行以一個方法開頭锁保,一個空格隔開薯酝,后面跟著請求的URI和協(xié)議的版本。格式如下Method Request-URI HTTP-Version CRLF爽柒。其中Method表示請求方法吴菠,Request-URI是一個統(tǒng)一資源標(biāo)識符,HTTP-Version表示請求的HTTP協(xié)議版本浩村,CRLF表示回車和換行(除了作為結(jié)尾的CRLF之外做葵,不允許出現(xiàn)單獨的CR或LF字符)HTTP響應(yīng)也是由三個部分組成,分別是:狀態(tài)行心墅、消息報頭(響應(yīng)頭)酿矢、響應(yīng)正文。響應(yīng)頭和響應(yīng)正文中間有一個空行怎燥。狀態(tài)行的格式如下:HTTP-Version Status-Code Reason-Phrase CRLF棠涮。其中,HTTP-Version表示服務(wù)器HTTP協(xié)議的版本刺覆,Status-Code表示服務(wù)器發(fā)回的響應(yīng)狀態(tài)碼严肪,Reason-phrase表示狀態(tài)代碼的文本描述。說了這么多可能你看的還是有點暈谦屑,通過下面這張圖你應(yīng)該能看得更清楚一點:
OK驳糯,我們上面說了瀏覽器可以當(dāng)做是一個客戶端,那么我們只需要寫一個服務(wù)端就行了氢橙。下面的代碼只是簡單的模擬了請求靜態(tài)資源和處理一個簡單的Servlet窍育。
服務(wù)端代碼如下:
package com.zkn.imitate.tomcat.secondchapter.first;
import com.zkn.imitate.tomcat.secondchapter.Request;
import com.zkn.imitate.tomcat.secondchapter.Response;
import com.zkn.imitate.tomcat.secondchapter.StaticResourceProcessor;
import com.zkn.imitate.tomcat.utils.Constants;
import com.zkn.imitate.tomcat.utils.StringUtil;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by wb-zhangkenan on 2016/12/29.
*/
public class HttpServer {
public static void main(String[] args){
await();
}
private static void await() {
ServerSocket serverSocket = null;
try {
boolean shutDown = false;
//創(chuàng)建一個服務(wù)端
serverSocket = new ServerSocket(8004,1, InetAddress.getByName("127.0.0.1"));
while (!shutDown){
//接收客戶端請求
Socket socket = serverSocket.accept();
Request request = new Request(socket.getInputStream());
request.parseRequest();//解析請求信息
Response response = new Response(socket.getOutputStream());
String uri = request.getUri();
if(uri !=null && uri.startsWith("/favicon.ico")){
}else if(!StringUtil.isEmpty(uri) && uri.startsWith("/static/")){
StaticResourceProcessor resouce = new StaticResourceProcessor();
resouce.process(request,response);//處理靜態(tài)資源
}else{
ServletProcessor servletProcessor = new ServletProcessor();
servletProcessor.process(request,response);//處理Servlet
}
socket.close();
shutDown = Constants.SHUT_DOWN.equals(request.getUri());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
模擬的Request主要代碼如下:
public class Request implements ServletRequest {
/**
* 輸入流
*/
private InputStream inputStream;
/**
* uri
*/
private String uri;
public Request(InputStream inputStream) {
this.inputStream = inputStream;
}
public void parseRequest(){
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String str = null;
try {
while((str = br.readLine()) != null){
if("".equals(str))
break;
sb.append(str).append("\n");
}
System.out.println(sb.toString());
uri = StringUtil.parserUri(sb.toString()," ");
} catch (IOException e) {
e.printStackTrace();
}
}
模擬的Response主要代碼如下:
public class Response implements ServletResponse {
/**
* 輸出流
*/
private OutputStream outputStream;
/**
* 字符輸出流
*/
private PrintWriter printWriter;
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void sendStaticResource(String path) {
FileInputStream fis = null;
try {
File file = new File(Constants.WEB_PATH, path);
if (file.exists() && !file.isDirectory()) {
if (file.canRead()) {
fis = new FileInputStream(file);
int flag = 0;
byte[] bytes = new byte[1024];
while ((flag = fis.read(bytes)) != -1){
outputStream.write(bytes);
}
}
}else{
PrintWriter printWriter = getWriter();
//這里用PrintWriter字符輸出流,設(shè)置自動刷新
printWriter.write("HTTP/1.1 404 File Not Found \r\n");
printWriter.write("Content-Type: text/html\r\n");
printWriter.write("Content-Length: 23\r\n");
printWriter.write("\r\n");
printWriter.write("<h1>File Not Found</h1>");
printWriter.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fis != null)
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
靜態(tài)資源處理類:
public class StaticResourceProcessor {
public void process(Request request,Response response){
response.sendStaticResource(request.getUri());
}
}
Servlet處理類如下:
public class FirstServlet implements Servlet{
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
PrintWriter out = servletResponse.getWriter();
out.println("Hello. Roses are red.");
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
ServletProcessor的代碼如下:
package com.zkn.imitate.tomcat.secondchapter.first;
import com.zkn.imitate.tomcat.secondchapter.Request;
import com.zkn.imitate.tomcat.secondchapter.Response;
import com.zkn.imitate.tomcat.utils.Constants;
import com.zkn.imitate.tomcat.utils.StringUtil;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
/**
* Created by wb-zhangkenan on 2016/12/29.
*/
public class ServletProcessor {
/**
* 處理請求信息
* @param request
* @param response
*/
public void process(Request request, Response response){
String str = request.getUri();
String servletName = null;
if(!StringUtil.isEmpty(str) && str.lastIndexOf("/") >= 0){
servletName = str.substring(str.lastIndexOf("/")+1);
}
URLClassLoader classLoader = null;
URL[] url = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
try {
//創(chuàng)建倉庫
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
url[0] = new URL(null,repository,streamHandler);
classLoader = new URLClassLoader(url);//URL類加載器
Class clazz = classLoader.loadClass(servletName);
Servlet servlet = (Servlet) clazz.newInstance();
servlet.service(request,response);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
下面我們看一下請求結(jié)果如何:
當(dāng)我們輸入:http://localhost:8004/FirstServlet時额划,請求結(jié)果如下所示:
當(dāng)我們輸入:http://localhost:8004/static/image01.jpg時,請求結(jié)果如下所示:
從上面的結(jié)果可以看到袍患,我們做的簡單的web服務(wù)器可以正常工作了。當(dāng)然了這個只是最簡單的web服務(wù)器了竣付,以后我會把這個寫的完整一些強(qiáng)大一些诡延。完整的代碼請從這里下載:https://github.com/zhangconan/ImitateTomCat