創(chuàng)建一個簡單的web服務(wù)器

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)該能看得更清楚一點:

請求
響應(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é)果如下所示:

FirstServlet

當(dāng)我們輸入:http://localhost:8004/static/image01.jpg時,請求結(jié)果如下所示:

static

從上面的結(jié)果可以看到袍患,我們做的簡單的web服務(wù)器可以正常工作了。當(dāng)然了這個只是最簡單的web服務(wù)器了竣付,以后我會把這個寫的完整一些強(qiáng)大一些诡延。完整的代碼請從這里下載:https://github.com/zhangconan/ImitateTomCat

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市古胆,隨后出現(xiàn)的幾起案子肆良,更是在濱河造成了極大的恐慌,老刑警劉巖逸绎,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惹恃,死亡現(xiàn)場離奇詭異,居然都是意外死亡棺牧,警方通過查閱死者的電腦和手機(jī)巫糙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颊乘,“玉大人参淹,你說我怎么就攤上這事∑G#” “怎么了承二?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纲爸。 經(jīng)常有香客問我亥鸠,道長,這世上最難降的妖魔是什么识啦? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任负蚊,我火速辦了婚禮,結(jié)果婚禮上颓哮,老公的妹妹穿的比我還像新娘家妆。我一直安慰自己,他們只是感情好冕茅,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布伤极。 她就那樣靜靜地躺著,像睡著了一般姨伤。 火紅的嫁衣襯著肌膚如雪哨坪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天乍楚,我揣著相機(jī)與錄音当编,去河邊找鬼。 笑死徒溪,一個胖子當(dāng)著我的面吹牛忿偷,可吹牛的內(nèi)容都是我干的金顿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼鲤桥,長吁一口氣:“原來是場噩夢啊……” “哼揍拆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芜壁,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤礁凡,失蹤者是張志新(化名)和其女友劉穎高氮,沒想到半個月后慧妄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡剪芍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年塞淹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罪裹。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡饱普,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出状共,到底是詐尸還是另有隱情套耕,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布峡继,位于F島的核電站冯袍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏碾牌。R本人自食惡果不足惜康愤,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舶吗。 院中可真熱鬧征冷,春花似錦、人聲如沸誓琼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腹侣。三九已至叔收,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐带,已是汗流浹背今穿。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留伦籍,地道東北人蓝晒。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓腮出,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芝薇。 傳聞我的和親對象是個殘疾皇子胚嘲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)洛二,斷路器馋劈,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 其它參考地址HTTP狀態(tài)碼引言HTTP是一個屬于應(yīng)用層的面向?qū)ο蟮膮f(xié)議,由于其簡捷晾嘶、快速的方式妓雾,適用于分布式超媒體...
    daf89232a846閱讀 1,116評論 0 5
  • 引言 HTTP是一個屬于應(yīng)用層的面向?qū)ο蟮膮f(xié)議,由于其簡捷垒迂、快速的方式械姻,適用于分布式超媒體信息系統(tǒng)。它于1990年...
    _燴面_閱讀 1,323評論 0 9
  • 社會是功利的机断,在這個功利的社會楷拳,我們沒有選擇,只有面對吏奸!面對欢揖! 我們提倡公平,但是在這個社會上又有多少公平呢...
    雨預(yù)閱讀 142評論 0 1
  • 亦喜亦悲 在懸崖下領(lǐng)悟真實 不看透怎敢沒入生死 借一段蹣跚的梯子 脈搏仍然自如 不知生命之苦 折射的月光里 清冷夜...
    雋永之歌閱讀 267評論 1 3