深入理解 Tomcat(三)Tomcat 底層實(shí)現(xiàn)原理

又是一個(gè)周末,這篇文章將從一個(gè)簡(jiǎn)單的例子來(lái)理解tomcat的底層設(shè)計(jì);

本文將介紹 Java Web 服務(wù)器是如何運(yùn)行的, Web 服務(wù)器也稱(chēng)為超文本傳輸協(xié)議( HyperText Transfer Protocol, HTTP)服務(wù)器, 因?yàn)樗褂?Http 與其客戶(hù)端(通常是 Web 瀏覽器)進(jìn)行通信, 基于 Java 的 Web 服務(wù)器會(huì)使用兩個(gè)重要的類(lèi): java.net.Socket 類(lèi)和 java.net.ServerSocket 類(lèi), 并通過(guò)發(fā)送 Http 消息進(jìn)行通信. 我們先花一些篇幅介紹 Http 協(xié)議(如果同學(xué)們熟悉HTTP協(xié)議可直接跳過(guò))和這兩個(gè)類(lèi), 然后寫(xiě)一個(gè)簡(jiǎn)單的 Web 服務(wù)器.

HTTP

Http : Http 允許 Web 服務(wù)器和瀏覽器通過(guò) Internet 發(fā)送并接受數(shù)據(jù), 是一種基于"請(qǐng)求---響應(yīng)"的協(xié)議, 客戶(hù)端請(qǐng)求一個(gè)文件, 服務(wù)器端對(duì)該請(qǐng)求進(jìn)行響應(yīng). Http 使用可靠的 tcp 連接, tcp 協(xié)議默認(rèn)使用 tcp 80端口, http協(xié)議的第一個(gè)版本是 http/0.9, 后來(lái)被 http/1.0取代, 隨后 http/1.0又被http/1.1取代, http/1.1 定義域 RFC(Request for Comment, 請(qǐng)求注解)2616中.

如果各位對(duì) Http1.1 有更多興趣, 請(qǐng)閱讀 RFC 2616.

在 Http 中, 總是由客戶(hù)端通過(guò)建立連接并發(fā)送 http 請(qǐng)求來(lái)初始化一個(gè)事務(wù)的. Web 服務(wù)器端并不負(fù)責(zé)聯(lián)系客戶(hù)端或建立一個(gè)到客戶(hù)端的回調(diào)連接.客戶(hù)端或服務(wù)器端可提前關(guān)閉連接, 例如, 當(dāng)使用 Web 瀏覽器瀏覽網(wǎng)頁(yè)時(shí), 可以單擊瀏覽器上的 stop 按鈕來(lái)停止下載文件, 這樣就有效的關(guān)閉了一個(gè) Web 服務(wù)器的 http 連接.

HTTP 請(qǐng)求

一個(gè) HTTP 請(qǐng)求包含以下三部分:

  • 請(qǐng)求方法----統(tǒng)一資源標(biāo)識(shí)符(Uniform Resource Identifier, URI)------協(xié)議/版本
  • 請(qǐng)求頭
  • 實(shí)體

下面是一個(gè) HTTP 請(qǐng)求的例子:

POST /examples/default.jsp HTTP/1.1 
Accept: text/plain; text/html 
Accept-Language: en-gb 
Connection: Keep-Alive 
Host: localhost 
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate 

lastName=Franks&firstName=Michael  

方法—統(tǒng)一資源標(biāo)識(shí)符(URI)—協(xié)議/版本出現(xiàn)在請(qǐng)求的第一行记餐。
POST /examples/default.jsp HTTP/1.1

這里 POST 是請(qǐng)求方法固惯,/examples/default.jsp 是 URI,而 HTTP/1.1 是協(xié)議/版本部分洛心。 每個(gè) HTTP 請(qǐng)求可以使用 HTTP 標(biāo)準(zhǔn)里邊提到的多種方法之一细燎。HTTP 1.1 支持 7 種類(lèi)型的請(qǐng) 求:GET, POST, HEAD, OPTIONS, PUT, DELETE 和 TRACE。GET 和 POST 在互聯(lián)網(wǎng)應(yīng)用里邊最普遍使用的皂甘。

URI 完全指明了一個(gè)互聯(lián)網(wǎng)資源玻驻。URI 通常是相對(duì)服務(wù)器的根目錄解釋的。因此偿枕,始終一斜 線(xiàn)/開(kāi)頭璧瞬。統(tǒng)一資源定位器(URL)其實(shí)是一種 URI(查看 http://www.ietf.org/rfc/rfc2396.txt)
來(lái)的。該協(xié)議版本代表了正在使用的 HTTP 協(xié)議的版本渐夸。

請(qǐng)求的頭部包含了關(guān)于客戶(hù)端環(huán)境和請(qǐng)求的主體內(nèi)容的有用信息嗤锉。例如它可能包括瀏覽器設(shè) 置的語(yǔ)言,主體內(nèi)容的長(zhǎng)度等等墓塌。每個(gè)頭部通過(guò)一個(gè)回車(chē)換行符(CRLF)來(lái)分隔的瘟忱。

對(duì)于 HTTP 請(qǐng)求格式來(lái)說(shuō),頭部和主體內(nèi)容之間有一個(gè)回車(chē)換行符(CRLF)是相當(dāng)重要的苫幢。CRLF 告訴HTTP服務(wù)器主體內(nèi)容是在什么地方開(kāi)始的访诱。在一些互聯(lián)網(wǎng)編程書(shū)籍中,CRLF還被認(rèn)為是HTTP 請(qǐng)求的第四部分韩肝。

在前面一個(gè) HTTP 請(qǐng)求中触菜,主體內(nèi)容只不過(guò)是下面一行:

lastName=Franks&firstName=Michael

實(shí)體內(nèi)容在一個(gè)典型的 HTTP 請(qǐng)求中可以很容易的變得更長(zhǎng)。

HTTP 響應(yīng)

類(lèi)似于 HTTP 請(qǐng)求哀峻,一個(gè) HTTP 響應(yīng)也包括三個(gè)組成部分:

  • 方法—統(tǒng)一資源標(biāo)識(shí)符(URI)—協(xié)議/版本
  • 響應(yīng)的頭部
  • 主體內(nèi)容

下面是一個(gè) HTTP 響應(yīng)的例子:

HTTP/1.1 200 OK 
Server: Microsoft-IIS/4.0 
Date: Mon, 5 Jan 2004 13:13:33 GMT 
Content-Type: text/html 
Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT 
Content-Length: 112 
 
<html> 
    <head> 
        <title>HTTP Response Example</title> 
    </head> 
    <body> 
        Welcome to Brainy Software 
    </body> 
</html> 

響應(yīng)頭部的第一行類(lèi)似于請(qǐng)求頭部的第一行涡相。第一行告訴你該協(xié)議使用 HTTP 1.1哲泊,請(qǐng)求成 功(200=成功),表示一切都運(yùn)行良好催蝗。

響應(yīng)頭部和請(qǐng)求頭部類(lèi)似械筛,也包括很多有用的信息抖所。響應(yīng)的主體內(nèi)容是響應(yīng)本身的 HTML 內(nèi) 容。頭部和主體內(nèi)容通過(guò) CRLF 分隔開(kāi)來(lái)。

Socket 類(lèi)

套接字是網(wǎng)絡(luò)連接的一個(gè)端點(diǎn)哄孤。套接字使得一個(gè)應(yīng)用可以從網(wǎng)絡(luò)中讀取和寫(xiě)入數(shù)據(jù)蜈项。放在兩 個(gè)不同計(jì)算機(jī)上的兩個(gè)應(yīng)用可以通過(guò)連接發(fā)送和接受字節(jié)流掌呜。為了從你的應(yīng)用發(fā)送一條信息到另 一個(gè)應(yīng)用毫捣,你需要知道另一個(gè)應(yīng)用的 IP 地址和套接字端口。在 Java 里邊遍尺,套接字指的是java.net.Socket類(lèi)截酷。

要?jiǎng)?chuàng)建一個(gè)套接字,你可以使用 Socket 類(lèi)眾多構(gòu)造方法中的一個(gè)乾戏。其中一個(gè)接收主機(jī)名稱(chēng) 和端口號(hào):

public Socket (java.lang.String host, int port)

在這里主機(jī)是指遠(yuǎn)程機(jī)器名稱(chēng)或者 IP 地址迂苛,端口是指遠(yuǎn)程應(yīng)用的端口號(hào)。例如鼓择,要連接 yahoo.com 的 80 端口三幻,你需要構(gòu)造以下的 Socket 對(duì)象:

new Socket ("yahoo.com", 80);

一旦你成功創(chuàng)建了一個(gè) Socket 類(lèi)的實(shí)例,你可以使用它來(lái)發(fā)送和接受字節(jié)流呐能。要發(fā)送字節(jié) 流念搬,你首先必須調(diào)用Socket類(lèi)的getOutputStream方法來(lái)獲取一個(gè)java.io.OutputStream對(duì)象。 要 發(fā) 送 文 本 到 一 個(gè) 遠(yuǎn) 程 應(yīng) 用 摆出, 你 經(jīng) 常 要 從 返 回 的 OutputStream 對(duì) 象 中 構(gòu) 造 一 個(gè) java.io.PrintWriter 對(duì)象朗徊。要從連接的另一端接受字節(jié)流,你可以調(diào)用 Socket 類(lèi)的 getInputStream 方法用來(lái)返回一個(gè) java.io.InputStream 對(duì)象偎漫。

ServerSocket 類(lèi)

Socket 類(lèi)代表一個(gè)客戶(hù)端套接字爷恳,即任何時(shí)候你想連接到一個(gè)遠(yuǎn)程服務(wù)器應(yīng)用的時(shí)候你構(gòu) 造的套接字,現(xiàn)在象踊,假如你想實(shí)施一個(gè)服務(wù)器應(yīng)用温亲,例如一個(gè) HTTP 服務(wù)器或者 FTP 服務(wù)器,你 需要一種不同的做法杯矩。這是因?yàn)槟愕姆?wù)器必須隨時(shí)待命栈虚,因?yàn)樗恢酪粋€(gè)客戶(hù)端應(yīng)用什么時(shí) 候會(huì)嘗試去連接它。為了讓你的應(yīng)用能隨時(shí)待命菊碟,你需要使用 java.net.ServerSocket 類(lèi)节芥。這是 服務(wù)器套接字的實(shí)現(xiàn)。

ServerSocket 和 Socket 不同逆害,服務(wù)器套接字的角色是等待來(lái)自客戶(hù)端的連接請(qǐng)求头镊。一旦服 務(wù)器套接字獲得一個(gè)連接請(qǐng)求,它創(chuàng)建一個(gè) Socket 實(shí)例來(lái)與客戶(hù)端進(jìn)行通信魄幕。

要?jiǎng)?chuàng)建一個(gè)服務(wù)器套接字相艇,你需要使用 ServerSocket 類(lèi)提供的四個(gè)構(gòu)造方法中的一個(gè)。你 需要指定 IP 地址和服務(wù)器套接字將要進(jìn)行監(jiān)聽(tīng)的端口號(hào)纯陨。通常坛芽,IP 地址將會(huì)是 127.0.0.1,也 就是說(shuō)翼抠,服務(wù)器套接字將會(huì)監(jiān)聽(tīng)本地機(jī)器咙轩。服務(wù)器套接字正在監(jiān)聽(tīng)的 IP 地址被稱(chēng)為是綁定地址。 服務(wù)器套接字的另一個(gè)重要的屬性是 backlog阴颖,這是服務(wù)器套接字開(kāi)始拒絕傳入的請(qǐng)求之前活喊,傳 入的連接請(qǐng)求的最大隊(duì)列長(zhǎng)度。

其中一個(gè) ServerSocket 類(lèi)的構(gòu)造方法如下所示:

public ServerSocket(int port, int backLog, InetAddress bindingAddress); 

應(yīng)用程序

如果同學(xué)們下載過(guò)我們?cè)诘谝黄恼绿峁┑脑创a(How Tomcat Works)的話(huà)量愧, 我們可以看一看我們的目錄:

我們的 web 服務(wù)器應(yīng)用程序放在 cxs01.pyrmont(編譯的時(shí)候因?yàn)殄e(cuò)誤改名字了钾菊,也就懶得改回來(lái)了) 包里邊,由三個(gè)類(lèi)組成:

  • HttpServer
  • Request
  • Response

這個(gè)應(yīng)用程序的入口點(diǎn)(靜態(tài) main 方法)可以在 HttpServer 類(lèi)里邊找到偎肃。main 方法創(chuàng)建了 一個(gè) HttpServer 的實(shí)例并調(diào)用了它的 await 方法煞烫。await 方法,顧名思義就是在一個(gè)指定的端 口上等待 HTTP 請(qǐng)求,處理它們并發(fā)送響應(yīng)返回客戶(hù)端累颂。它一直等待直至接收到 shutdown 命令滞详。

應(yīng)用程序不能做什么,除了發(fā)送靜態(tài)資源紊馏,例如放在一個(gè)特定目錄的 HTML 文件和圖像文件料饥。 它也在控制臺(tái)上顯示傳入的 HTTP 請(qǐng)求的字節(jié)流。不過(guò)瘦棋,它不給瀏覽器發(fā)送任何的頭部例如日期 或者 cookies稀火。

下面我們來(lái)看看我們今天的重點(diǎn),這三個(gè)類(lèi)赌朋,也就是tomcat的雛形代碼
HttpServer 類(lèi)

HttpServer 類(lèi)代表一個(gè) web 服務(wù)器凰狞,也就是程序的入口,看代碼:

public class HttpServer {
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";

  // 關(guān)閉命令
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

  // 是否關(guān)閉
  private boolean shutdown = false;

  public static void main(String[] args) {
    HttpServer server = new HttpServer();
    server.await();
  }

main 方法中創(chuàng)建了一個(gè)HttpServer對(duì)象沛慢,并調(diào)用了該對(duì)象的await方法赡若。看名字团甲,該方法應(yīng)該是等待http請(qǐng)求之類(lèi)的東東逾冬。我們來(lái)看看方法內(nèi)部:

public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
      // 創(chuàng)建一個(gè)socket服務(wù)器
      serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
      e.printStackTrace();
      System.exit(1);
    }

    // 循環(huán)等待http請(qǐng)求
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
        // 阻塞等待http請(qǐng)求
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();

        // 創(chuàng)建一個(gè)Request對(duì)象用于解析http請(qǐng)求內(nèi)容
        Request request = new Request(input);
        request.parse();

        // 創(chuàng)建一個(gè)Response 對(duì)象,用于發(fā)送靜態(tài)文本
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource();

        // 關(guān)閉流
        socket.close();

        //檢查URI中是否有關(guān)閉命令
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        continue;
      }
    }
  }

我們看到,該方法創(chuàng)建了一個(gè)Socket服務(wù)器身腻,并循環(huán)阻塞監(jiān)聽(tīng)http請(qǐng)求产还,當(dāng)有http請(qǐng)求到來(lái)時(shí), 該方法便創(chuàng)建一個(gè)Request對(duì)象嘀趟,構(gòu)造參數(shù)是socket獲取的輸入流對(duì)象脐区, 用于讀取客戶(hù)端請(qǐng)求的數(shù)據(jù)并解析。 然后再創(chuàng)建一個(gè)Response對(duì)象她按,構(gòu)造參數(shù)是socket的輸出流對(duì)象牛隅, 并含有一個(gè)Request對(duì)象的成員變量。Response對(duì)象用于將靜態(tài)頁(yè)面發(fā)送給瀏覽器或者是其他的客戶(hù)端酌泰。最后媒佣, 該方法校驗(yàn)請(qǐng)求中是否含有關(guān)閉命令的字符串,如果有陵刹,就停止服務(wù)器的運(yùn)行默伍。

這就是一個(gè)簡(jiǎn)單的服務(wù)器, 當(dāng)我第一次看到的時(shí)候授霸,心想: 真TMD簡(jiǎn)單啊巡验。原來(lái)沒(méi)那么復(fù)雜嘛。我想同學(xué)們心里想的跟我也一樣吧碘耳。so显设, 不論多么龐大的代碼,底層原理都是很簡(jiǎn)單的辛辨,只要我們學(xué)好了基礎(chǔ)捕捂,學(xué)習(xí)起來(lái)就會(huì)輕松很多。

廢話(huà)不多說(shuō)斗搞,我們繼續(xù)看看Request 是如何解析Http請(qǐng)求的吧指攒。

Request 類(lèi)

類(lèi)結(jié)構(gòu)圖如下:


image

Request 類(lèi)代表一個(gè) HTTP 請(qǐng)求。從負(fù)責(zé)與客戶(hù)端通信的 Socket 中傳遞過(guò)來(lái) InputStream 對(duì)象來(lái)構(gòu)造這個(gè)類(lèi)的一個(gè)實(shí)例僻焚。你調(diào)用 InputStream 對(duì)象其中一個(gè) read 方法來(lái)獲 取 HTTP 請(qǐng)求的原始數(shù)據(jù)允悦。其中最主要的方法就是parse 和 parseUri ,他們用于逐個(gè)解析每個(gè)從客戶(hù)端傳遞過(guò)來(lái)的字節(jié)虑啤,我們先看parse方法:

  public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    try {
      // 讀取流中內(nèi)容
      i = input.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
     // 將每個(gè)字節(jié)轉(zhuǎn)換為字符
      request.append((char) buffer[j]);
    }
    // 打印字符串
    System.out.print(request.toString());
    // 根據(jù)轉(zhuǎn)換出來(lái)的字符解析URI
    uri = parseUri(request.toString());
  }

我們也看到該方法是十分的簡(jiǎn)單隙弛, 創(chuàng)建一個(gè)StringBuffer 對(duì)象,然后從流中讀取字節(jié)狞山,然后循環(huán)將字節(jié)轉(zhuǎn)成字符寫(xiě)入到Stringbuffer對(duì)象中全闷。最后傳入到parseUri方法中進(jìn)行解析。

我們?cè)倏纯磒arseUri方法萍启, 這個(gè)方法中总珠,我們前面學(xué)習(xí)的關(guān)于HTTP的知識(shí)會(huì)起作用:

  private String parseUri(String requestString) {
    int index1, index2;
    // 找到第一個(gè)空格
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      // 找到第二個(gè)空格
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        // 截取第一個(gè)空格到第二個(gè)空格之間的內(nèi)容
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

該方法從請(qǐng)求行里邊獲得 URI屏鳍。parseUri 方法搜索請(qǐng)求里邊的第一個(gè)和第二個(gè)空格并從中獲取 URI。
為什么是第一個(gè)空格和第二個(gè)空格之間的內(nèi)容呢局服?我們看看前面的Http請(qǐng)求的例子:

POST /examples/default.jsp HTTP/1.1 
Accept: text/plain; text/html 
Accept-Language: en-gb 
Connection: Keep-Alive 
Host: localhost 
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate 

lastName=Franks&firstName=Michael  

我們看第一行:


POST 和 HTTP/1.1之間的就是我們需要的URI钓瞭。so, 我們只需要將中間那段字符串截取就OK了腌逢。

我們總結(jié)一下Request類(lèi)降淮,這個(gè)類(lèi)其實(shí)就是解析HTTP 消息頭內(nèi)容的超埋,先將流中數(shù)據(jù)轉(zhuǎn)成字節(jié)搏讶,然后將轉(zhuǎn)成字符,最后將字符解析霍殴,得到自己感興趣的內(nèi)容媒惕。奏是這么簡(jiǎn)單。好了来庭,我們?cè)倏纯碦esponse類(lèi)妒蔚。看看他是怎么實(shí)現(xiàn)的月弛。

Response類(lèi)

我們先看看這個(gè)類(lèi)的結(jié)構(gòu)圖:


Response 代表了Http請(qǐng)求中的一個(gè)響應(yīng)肴盏。我們關(guān)注其中的 sendStaticResource 方法,看名字帽衙,該方法應(yīng)該是發(fā)送靜態(tài)資源給客戶(hù)端菜皂。我們看看代碼:

  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 {
        // 沒(méi)有文件返回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) {
      // thrown if cannot instantiate a File object
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }

可以看到,該方法也非常的簡(jiǎn)單厉萝, sendStaticResource 方法是用來(lái)發(fā)送一個(gè)靜態(tài)資源恍飘,例如一個(gè) HTML 文件。它首先通過(guò)傳遞 上一級(jí)目錄的路徑和子路徑給 File 累的構(gòu)造方法來(lái)實(shí)例化 java.io.File 類(lèi)谴垫。

然后它檢查該文件是否存在章母。假如存在的話(huà),通過(guò)傳遞 File 對(duì)象讓 sendStaticResource 構(gòu)造一個(gè) java.io.FileInputStream 對(duì)象翩剪。然后乳怎,它調(diào)用 FileInputStream 的 read 方法并把字 節(jié)數(shù)組寫(xiě)入 OutputStream 對(duì)象。請(qǐng)注意前弯,這種情況下蚪缀,靜態(tài)資源是作為原始數(shù)據(jù)發(fā)送給瀏覽器 的。

假如文件并不存在博杖,sendStaticResource 方法發(fā)送一個(gè)錯(cuò)誤信息到瀏覽器

運(yùn)行程序椿胯,啟動(dòng)HttpServer mian方法,使用Edge瀏覽器在地址欄敲入:http://localhost:8080/index.html
返回:

表示文件存在剃根, 再看看我們的后臺(tái)控制臺(tái):


如期打印了http請(qǐng)求頭中的內(nèi)容哩盲。并且下面還請(qǐng)求了一張圖片。

總結(jié)

至此,我們已經(jīng)知道了一個(gè)簡(jiǎn)單的Web服務(wù)器是如何工作的廉油。破除了我們之前的疑惑惠险,實(shí)際上tomcat底層就是這么實(shí)現(xiàn)的,可能關(guān)于阻塞IO和非阻塞NIO會(huì)有區(qū)別抒线,但總體上還是這個(gè)思路班巩,然后其余的組件都是針對(duì)優(yōu)化性能,提高擴(kuò)展性來(lái)設(shè)計(jì)新的架構(gòu)嘶炭。所以抱慌,我們明白了底層設(shè)計(jì),再去學(xué)習(xí)他的設(shè)計(jì)眨猎,就不會(huì)那么迷茫抑进。從而感到泄氣。畢竟每個(gè)夜晚睡陪,我們孤獨(dú)的學(xué)習(xí)寺渗,不想徒勞無(wú)功。

好了兰迫,本文結(jié)束P攀狻!汁果! good luck N芯小!须鼎!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鲸伴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晋控,更是在濱河造成了極大的恐慌汞窗,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赡译,死亡現(xiàn)場(chǎng)離奇詭異仲吏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蝌焚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)裹唆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人只洒,你說(shuō)我怎么就攤上這事许帐。” “怎么了毕谴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵成畦,是天一觀(guān)的道長(zhǎng)距芬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)循帐,這世上最難降的妖魔是什么框仔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮拄养,結(jié)果婚禮上离斩,老公的妹妹穿的比我還像新娘。我一直安慰自己瘪匿,他們只是感情好跛梗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著柿顶,像睡著了一般茄袖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嘁锯,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音聂薪,去河邊找鬼家乘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛藏澳,可吹牛的內(nèi)容都是我干的仁锯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翔悠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼业崖!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蓄愁,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤双炕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后撮抓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體妇斤,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年丹拯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了站超。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乖酬,死狀恐怖死相,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咬像,我是刑警寧澤算撮,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布双肤,位于F島的核電站,受9級(jí)特大地震影響钮惠,放射性物質(zhì)發(fā)生泄漏茅糜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一素挽、第九天 我趴在偏房一處隱蔽的房頂上張望蔑赘。 院中可真熱鬧,春花似錦预明、人聲如沸缩赛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酥馍。三九已至,卻和暖如春阅酪,著一層夾襖步出監(jiān)牢的瞬間旨袒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工术辐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砚尽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓辉词,卻偏偏與公主長(zhǎng)得像必孤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瑞躺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理敷搪,服務(wù)發(fā)現(xiàn),斷路器幢哨,智...
    卡卡羅2017閱讀 134,693評(píng)論 18 139
  • 1赡勘、OC中創(chuàng)建線(xiàn)程的方法是什么?如果指定在主線(xiàn)程中執(zhí)行代碼嘱么?如何延時(shí)執(zhí)行代碼狮含。【難度系數(shù)★★】 1)創(chuàng)建線(xiàn)程的方法...
    木旁_G閱讀 1,964評(píng)論 2 16
  • 多線(xiàn)程曼振、特別是NSOperation 和 GCD 的內(nèi)部原理几迄。運(yùn)行時(shí)機(jī)制的原理和運(yùn)用場(chǎng)景。SDWebImage的原...
    LZM輪回閱讀 2,009評(píng)論 0 12
  • 參考:http://www.2cto.com/net/201611/569006.html TCP HTTP UD...
    F麥子閱讀 2,951評(píng)論 0 14
  • 審美觀(guān)的審美對(duì)象范圍不只局限在繪畫(huà)和藝術(shù)里解孙,因?yàn)槊朗菬o(wú)處不在的坑填。一個(gè)人審美觀(guān)的形成伴隨著他的成長(zhǎng),隨著他越長(zhǎng)越大弛姜,...
    lilywright閱讀 178評(píng)論 0 0