成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

一步一步實(shí)現(xiàn)Tomcat之一——實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Web服務(wù)器

yearsj / 3190人閱讀

摘要:原書(shū)中主要內(nèi)容是一步一步實(shí)現(xiàn)一個(gè)類(lèi)似于的容器。圖一協(xié)議處于協(xié)議棧的應(yīng)用層,傳遞的內(nèi)容是報(bào)文,報(bào)文就相當(dāng)于語(yǔ)言中的短語(yǔ)和句子用來(lái)表明意圖。類(lèi)表示一次客戶(hù)端請(qǐng)求解析請(qǐng)求待實(shí)現(xiàn)解析待實(shí)現(xiàn)類(lèi)表示返回值發(fā)送靜態(tài)頁(yè)面的相應(yīng)報(bào)文待實(shí)現(xiàn)。

前言

最近在讀《How Tomcat Works》,收獲頗豐,在編寫(xiě)書(shū)中示例的過(guò)程中也踩了不少坑。不知你有沒(méi)有體會(huì),編程就一門(mén)是“不試不知道,一試嚇一跳”的實(shí)踐藝術(shù)。所以我將將自己的實(shí)踐過(guò)程記錄下來(lái)并附上自己的思想過(guò)程編撰成文,望能拋磚引玉,引起大家思考。
原書(shū)中主要內(nèi)容是一步一步實(shí)現(xiàn)一個(gè)類(lèi)似于Tomcat的Servlet容器。有點(diǎn)再造輪子的感覺(jué),我也會(huì)根據(jù)書(shū)中章節(jié)并按照自己理解分步成文。

本文涉及內(nèi)容

本文描述了一個(gè)簡(jiǎn)單的Web服務(wù)器的實(shí)現(xiàn),這個(gè)服務(wù)器能接收瀏覽器請(qǐng)求,訪問(wèn)本地的靜態(tài)HTML文件,如果文件不存在返回404頁(yè)面。這個(gè)瀏覽器只是一個(gè)示例,重點(diǎn)讓你了解Http請(qǐng)求到響應(yīng)過(guò)程的大致處理方法,對(duì)于細(xì)節(jié)沒(méi)有過(guò)多涉及。

基礎(chǔ)知識(shí)

閱讀本文需要你先了解一下基礎(chǔ)知識(shí):

Http協(xié)議。

Socket網(wǎng)絡(luò)編程。

1. Http協(xié)議

“協(xié)議”廣義上說(shuō)就是計(jì)算機(jī)相互交流的語(yǔ)言。Http協(xié)議就是網(wǎng)絡(luò)上千千萬(wàn)萬(wàn)瀏覽器和服務(wù)器交流的語(yǔ)言,瀏覽器通過(guò)Http協(xié)議向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器通過(guò)同樣的協(xié)議回復(fù)瀏覽器。

【圖一】

Http協(xié)議處于TCP/IP協(xié)議棧的應(yīng)用層,Http傳遞的內(nèi)容是Http報(bào)文,報(bào)文就相當(dāng)于語(yǔ)言中的“短語(yǔ)”和“句子”用來(lái)表明意圖。報(bào)文由一行行簡(jiǎn)單的字符串組成,方便人們讀寫(xiě)。

報(bào)文包括三個(gè)部分:起始行(star line)、首部(heads)、主體(body)
報(bào)文分為兩類(lèi):請(qǐng)求報(bào)文(request message)、響應(yīng)報(bào)文(response message)

報(bào)文實(shí)例:

請(qǐng)求報(bào)文:

GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Cookie: BAIDUID=DF436E68F85BD96DE35AEA9DC97FB19D:FG=1; BIDUPSID=DF436E68F85BD96DE35AEA9DC97FB19D; PSTM=1535160357; BD_UPN=1352; delPer=0; BD_HOME=0; H_PS_PSSID=1442_21097_26350
Connection: keep-alive

GET / HTTP/1.1為起始行,其他為首部,沒(méi)有主體部分。

響應(yīng)報(bào)文:

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xc317983b0005c39e
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html
Cxy_all: baidu+3d05fe4a15be8fad069c0f37a523ec5e
Date: Sun, 26 Aug 2018 06:39:25 GMT
Expires: Sun, 26 Aug 2018 06:39:09 GMT
Server: BWS/1.1
Set-Cookie: delPer=0; expires=Tue, 18-Aug-2048 06:39:09 GMT
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1442_21097_26350; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked





    
    
    
    
    
    百度一下,你就知道


太多了,省略...


HTTP/1.1 200 OK為起始行,Bdpagetype: 1Transfer-Encoding: chunked為首部,其余的為主體。

通過(guò)觀察請(qǐng)求和返回報(bào)文我們發(fā)現(xiàn)兩個(gè)關(guān)鍵點(diǎn):

報(bào)文起始行和首部由行分割的ASCII文本,Http協(xié)議規(guī)定每一行由回車(chē)符(ASCII碼13)和換行符(ASCII碼10)表示結(jié)束。

一個(gè)空白行將實(shí)體和首部區(qū)分開(kāi)來(lái),返回報(bào)文的主體的就是HTML語(yǔ)言,瀏覽器就是通過(guò)返回的主體內(nèi)容渲染HTML語(yǔ)言展示請(qǐng)求內(nèi)容的,當(dāng)然除了HTML語(yǔ)言之外,主體還可以返回其他字符和二進(jìn)制內(nèi)容。

2. Socket網(wǎng)絡(luò)編程

Http協(xié)議不僅規(guī)定了傳輸?shù)膬?nèi)容,還規(guī)定了用什么來(lái)傳輸,一門(mén)語(yǔ)言不能光有文字和語(yǔ)法,還要有傳播通道,例如空氣就是聲音的傳輸通道。

Http協(xié)議將傳輸?shù)墓ぷ鹘挥蒚CP協(xié)議負(fù)責(zé),TCP協(xié)議位于TCP/IP協(xié)議棧的傳輸層,是很多上層應(yīng)用協(xié)議的傳輸方式。

TCP協(xié)議是面向連接的、保障型傳輸協(xié)議,一旦建立起TCP連接,客戶(hù)端和服務(wù)器端之間的報(bào)文交換就不會(huì)丟失、不會(huì)被破壞也不會(huì)在接收時(shí)錯(cuò)序。

TCP協(xié)議一般由操作系統(tǒng)底層實(shí)現(xiàn),在Java中抽象為Socket接口供大家使用。

用代碼說(shuō)話(huà)

基礎(chǔ)知識(shí)介紹的差不多了,如果大家感興趣可以參考相應(yīng)的書(shū)籍。接下來(lái)讓我們用代碼說(shuō)話(huà)。

一、 看似很簡(jiǎn)單

如果是只返回靜態(tài)Html,應(yīng)該很簡(jiǎn)單吧。簡(jiǎn)簡(jiǎn)單單想了一下流程,初始化服務(wù)器——等待連接——解析請(qǐng)求——返回?cái)?shù)據(jù)——關(guān)閉連接,搞定,大功告成。

1. 建個(gè)服務(wù)器骨架吧
/**
 * 簡(jiǎn)單的Web服務(wù)器
 */
public class HttpServer {
    //定義一個(gè)資源存放路徑,用來(lái)存放靜態(tài)資源,
    public static final File WEB_ROOT = new File("d:webRoot");

    public static void main(String[] args) {
        //創(chuàng)建服務(wù)器對(duì)象
        HttpServer httpServer=new HttpServer();
        //等待客戶(hù)端請(qǐng)求
        httpServer.await();
    }
    public void await() {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            //創(chuàng)建socket嵌套字,監(jiān)聽(tīng)8080端口。
             serverProcess(serverSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void serverProcess(ServerSocket serverSocket) {
        while (true) {
            //循環(huán)等待客戶(hù)端請(qǐng)求。
            try (Socket socket = serverSocket.accept()) {
                InputStream input = socket.getInputStream();
                OutputStream output = socket.getOutputStream();
                //未完待續(xù)。。。

            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

非常簡(jiǎn)單的Socket服務(wù)器骨架就這樣建好了,我們就可以接受客戶(hù)端請(qǐng)求了,這里需要注意的是每一個(gè)通過(guò)serverSocket.accept()從客戶(hù)端獲取socket處理完后都會(huì)被close

2. 抽象一下“請(qǐng)求”和“響應(yīng)”

有了服務(wù)器,接下來(lái)我們需要接收請(qǐng)求、處理請(qǐng)求、將處理結(jié)果返回給客戶(hù)端。根據(jù)領(lǐng)域驅(qū)動(dòng)原則,我們將名詞抽象為類(lèi),動(dòng)詞抽象為類(lèi)的行為也就是方法。

Request類(lèi)

/**
 * 表示一次客戶(hù)端請(qǐng)求
 */
public class Request {
    private InputStream input;
    private String uri;

    public Request(InputStream input) {
        this.input = input;
    }
    /**
     * 解析請(qǐng)求
     */
    public void parse() {
        //待實(shí)現(xiàn)
    }
    /**
     * 解析URL
     * @param requestString
     * @return
     */
    private String parseUri(String requestString) {
        //待實(shí)現(xiàn)
        return null;
    }

    public String getUri() {
        return uri;
    }
}

Response類(lèi):

/**
 * 表示返回值
 */
public class Response {
    private OutputStream output;
    public Response1(OutputStream output) {
        this.output = output;
    }
    /**
     * 發(fā)送靜態(tài)頁(yè)面的相應(yīng)報(bào)文
     * @throws IOException
     */
    public void sendStaticResource() throws IOException {
        //待實(shí)現(xiàn)。
    }
}
3. 實(shí)現(xiàn)Request和Response中的方法。

類(lèi)和方法已經(jīng)定義的差不多了,現(xiàn)在我們來(lái)實(shí)現(xiàn)。

Request類(lèi)

/**
 * 表示請(qǐng)求值
 */
public class Request {

    private InputStream input;
    private String uri;

    public Request(InputStream input) {
        this.input = input;
    }

    public void parse() {
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
            while((i = input.read(buffer))!=-1){
                for (int j=0; j index1)
                return requestString.substring(index1 + 1, index2);
        }
        return null;
    }
    public String getUri() {
        return uri;
    }
}

Response類(lèi):

/**
 * 表示返回值
 */
public class Response {
    private static final int BUFFER_SIZE = 1024;
    private Request request;
    private 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];
        //讀取訪問(wèn)地址請(qǐng)求的文件
        File file = new File(HttpServer.WEB_ROOT, request.getUri());
        try (FileInputStream fis = new FileInputStream(file)){
            if (file.exists()) {
                //如果文件存在
                //添加相應(yīng)頭。
                StringBuilder heads=new StringBuilder("HTTP/1.1 200 OK
");
                heads.append("Content-Type: text/html
");
                //頭部
                StringBuilder body=new StringBuilder();
                //讀取相應(yīng)主體
                int len ;
                while ((len=fis.read(bytes, 0, BUFFER_SIZE)) != -1) {
                    body.append(new String(bytes,0,len));
                }
                //添加Content-Length
                heads.append(String.format("Content-Length: %d
",body.toString().getBytes().length));
                heads.append("
");
                output.write(heads.toString().getBytes());
                output.write(body.toString().getBytes());
            } else {
                response404(output);
            }
        }catch (FileNotFoundException e){
            response404(output);
        }
    }

    private void response404(OutputStream output) throws IOException {
        StringBuilder response=new StringBuilder();
        response.append("HTTP/1.1 404 File Not Found
");
        response.append("Content-Type: text/html
");
        response.append("Content-Length: 23
");
        response.append("
");
        response.append("

File Not Found

"); output.write(response.toString().getBytes()); }

注:原書(shū)代碼沒(méi)有返回響應(yīng)頭部,測(cè)試發(fā)現(xiàn)瀏覽器不能識(shí)別這樣的響應(yīng)報(bào)文。

4. 補(bǔ)全服務(wù)器方法。
public class HttpServer {
    //定義一個(gè)資源存放路徑,用來(lái)存放靜態(tài)資源,
    static final File WEB_ROOT = new File("d:webRoot");

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

    public void await() {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
           serverProcess(serverSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void serverProcess(ServerSocket serverSocket) {
        while (true) {
            try (Socket socket = serverSocket.accept()) {
                System.out.println(socket.hashCode());
                InputStream input = socket.getInputStream();
                OutputStream output = socket.getOutputStream();
                Request request = new Request(input);
                request.parse();
                Response response = new Response(output);
                response.setRequest(request);
                response.sendStaticResource();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5. 見(jiàn)證奇跡的時(shí)候到了,運(yùn)行一下。

首先在D:/webRoot文件夾建立index.html文件
寫(xiě)入:

hello world!

啟動(dòng)HttpService,在瀏覽器輸入http://localhost:8080/index.html,但你心心念的等待熟悉的“hello world!”頁(yè)面的時(shí)候,你會(huì)等的花兒都謝了。

二、 問(wèn)題在哪里? 1. 調(diào)試吧,少年

頁(yè)面并沒(méi)有顯示,問(wèn)題出在哪里?進(jìn)入debug調(diào)試模式,發(fā)現(xiàn)方法阻塞在while((i = input.read(buffer))!=-1)語(yǔ)句上,以往我們讀取輸入流的方法都這樣寫(xiě)也沒(méi)有問(wèn)題,為什么到了Socket就阻塞了呢?原因其實(shí)很簡(jiǎn)單,客戶(hù)打開(kāi)了一個(gè)socket的輸出流向服務(wù)器發(fā)送消息,服務(wù)器端通過(guò)socket的輸入流讀取消息,但是服務(wù)器并不知道客戶(hù)端消息的結(jié)尾,只要socket不關(guān)閉,服務(wù)器一旦讀取了所有可用內(nèi)容,read方法就要一直阻塞等待新的可用內(nèi)容(超期時(shí)間之后也能返回),而此時(shí)的客戶(hù)端也一直在等待服務(wù)器的返回,相互等待,死鎖了??磥?lái)本地文件流和網(wǎng)絡(luò)流處理方式不同。

【圖二】

翻看書(shū)中示例代碼是這樣寫(xiě)的:

public void parse() {
        StringBuilder request = new StringBuilder(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

書(shū)中一次性讀取了2048長(zhǎng)度的字節(jié)數(shù)組,無(wú)論請(qǐng)求內(nèi)容是否結(jié)束都不會(huì)再去讀第二遍,避免讀取時(shí)遇到不可用情況造成的阻塞。
但是這依然有兩個(gè)問(wèn)題:

如果字符請(qǐng)求內(nèi)容大于2048長(zhǎng)度字節(jié)數(shù)組的內(nèi)容,請(qǐng)求內(nèi)容讀取不全。

如果瀏覽器創(chuàng)建一個(gè)socket但是并不寫(xiě)入任何內(nèi)容,服務(wù)器首次read的時(shí)候仍會(huì)被阻塞,不讀取不知道有沒(méi)有內(nèi)容,一旦發(fā)現(xiàn)沒(méi)有可用內(nèi)容就被阻塞了。(測(cè)試中Chrome就會(huì)發(fā)送空socket)

問(wèn)題2還好,有可能瀏覽器通過(guò)發(fā)送空socket維持長(zhǎng)連接,需要根據(jù)http協(xié)議決定如何關(guān)閉socket。但是對(duì)于問(wèn)題1就比較嚴(yán)重了,雖然我們的示例代碼只需要讀取起始行從中取出URL地址訪問(wèn)本地靜態(tài)資源,但是一個(gè)web服務(wù)器服務(wù)讀取所有請(qǐng)求內(nèi)容確實(shí)有點(diǎn)說(shuō)不過(guò)去了。這個(gè)問(wèn)題后續(xù)還需要解決。

2. 再試試,有沒(méi)有奇跡出現(xiàn)

替換上面的代碼,再次重復(fù)剛剛的流程,好了,瀏覽器終于出現(xiàn)“hello world!”,見(jiàn)證奇跡。

三、 你以為這樣就完了?

終于,人生中第一個(gè)web服務(wù)器就這樣誕生了!當(dāng)我難掩激動(dòng)的用各個(gè)瀏覽器測(cè)試的時(shí)候,又發(fā)現(xiàn)的一個(gè)問(wèn)題,一旦我用Chrome訪問(wèn)一次,再用其他瀏覽器訪問(wèn)就會(huì)卡死。哎,好吧,沒(méi)完了。

1. 繼續(xù)debug

經(jīng)過(guò)debug發(fā)現(xiàn),Chrome每次發(fā)送一次socket并收到服務(wù)器相應(yīng)之后,都會(huì)發(fā)送一個(gè)新的空socket,socket沒(méi)有寫(xiě)入任何內(nèi)容,此時(shí)服務(wù)器就會(huì)阻塞在對(duì)這個(gè)空socket的讀取中。直到瀏覽器再次向服務(wù)器發(fā)送請(qǐng)求,才會(huì)向這個(gè)空socket寫(xiě)入內(nèi)容,服務(wù)器阻塞才會(huì)結(jié)束,然后繼續(xù)重復(fù)以上的處理過(guò)程,只要Chrome瀏覽器發(fā)送一次請(qǐng)求,服務(wù)器就會(huì)阻塞與空socket的讀取,無(wú)法為其他瀏覽器服務(wù)。

【圖三】

2. 飯要一口一口吃

除了上面提到的兩個(gè)問(wèn)題還有其他問(wèn)題,比如socket關(guān)閉時(shí)機(jī)問(wèn)題,響應(yīng)主體文字編碼問(wèn)(現(xiàn)在都是英文還好,中文就會(huì)出現(xiàn)亂碼)等等。畢竟http協(xié)議也是比較復(fù)雜的,有很多規(guī)則需要實(shí)現(xiàn)。但是本文的內(nèi)容就先到這了,我們實(shí)現(xiàn)了完成一個(gè)簡(jiǎn)單服務(wù)器的目標(biāo)。

后記

本文到此結(jié)束了,參照《How Tomcat Works》第一章內(nèi)容,加上自己的理解和實(shí)踐,原書(shū)中沒(méi)有涉及我調(diào)試中拋出的兩個(gè)問(wèn)題,關(guān)于這兩個(gè)問(wèn)題我會(huì)在以后的文章中解決。其實(shí)讀書(shū)的的時(shí)候覺(jué)得很簡(jiǎn)單,也沒(méi)有想到真正寫(xiě)代碼的時(shí)候出現(xiàn)這些問(wèn)題,所以希望大家讀書(shū)過(guò)程中多實(shí)踐,可以加深理解。作為專(zhuān)欄的第一篇文章,寫(xiě)的格外用心,但是也難免出現(xiàn)紕漏,望大家指摘。

源碼

文中源碼地址:https://github.com/TmTse/tiny...

參考

《深入剖析Tomcat》
《Http權(quán)威指南》
《TCP/IP詳解卷1:協(xié)議》

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/76860.html

相關(guān)文章

  • 步一實(shí)現(xiàn)Tomcat之二——實(shí)現(xiàn)一個(gè)簡(jiǎn)單Servlet容器

    摘要:注本文使用規(guī)范是規(guī)范中的一個(gè)接口,我們可以自己實(shí)現(xiàn)這個(gè)接口在方法中實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。我們只是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的容器示例,所以和其他方法留待以后實(shí)現(xiàn)。運(yùn)行一下實(shí)現(xiàn)首先編寫(xiě)一個(gè)自己的實(shí)現(xiàn)類(lèi)。 前言 經(jīng)過(guò)上一篇文章《一步一步實(shí)現(xiàn)Tomcat——實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Web服務(wù)器》,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Web服務(wù)器,可以響應(yīng)瀏覽器請(qǐng)求顯示靜態(tài)Html頁(yè)面,本文更進(jìn)一步,實(shí)現(xiàn)一個(gè)Servlet容器,我們不...

    dayday_up 評(píng)論0 收藏0
  • 步一實(shí)現(xiàn)web程序信息管理系統(tǒng)之一----登陸界面實(shí)現(xiàn)

    摘要:一步一步實(shí)現(xiàn)程序信息管理系統(tǒng)一步一步實(shí)現(xiàn)程序信息管理系統(tǒng)在程序中特別是信息管理系統(tǒng),登陸功能必須有而且特別重要。每一個(gè)學(xué)習(xí)程序開(kāi)發(fā)或以后工作中,都會(huì)遇到實(shí)現(xiàn)登陸功能的需求。本篇記錄一下登陸功能的前端界面的實(shí)現(xiàn)。一步一步實(shí)現(xiàn)web程序信息管理系統(tǒng) 在web程序中特別是信息管理系統(tǒng),登陸功能必須有而且特別重要。每一個(gè)學(xué)習(xí)程序開(kāi)發(fā)或以后工作中,都會(huì)遇到實(shí)現(xiàn)登陸功能的需求。而登陸功能最終提供給客戶(hù)或...

    sixgo 評(píng)論0 收藏0
  • JavaEE環(huán)境配置與示例教程

    摘要:環(huán)境配置運(yùn)行環(huán)境安裝配置數(shù)據(jù)庫(kù)下載安裝下載地址牢記安裝過(guò)程中設(shè)置的用戶(hù)的密碼安裝選擇版本的安裝配置數(shù)據(jù)庫(kù)驅(qū)動(dòng)教程前提開(kāi)發(fā)環(huán)境參考環(huán)境配置文檔基礎(chǔ)知識(shí)基本語(yǔ)法協(xié)議基礎(chǔ)知識(shí)只需了解請(qǐng)求即可基礎(chǔ)的等。 **寒假的時(shí)候老師讓寫(xiě)個(gè)簡(jiǎn)單的JavaEE教程給學(xué)弟or學(xué)妹看,于是寫(xiě)了下面的內(nèi)容。發(fā)表到這個(gè)地方以防丟失。。。因?yàn)閷?xiě)的時(shí)候用的是word,直接復(fù)制過(guò)來(lái)格式有點(diǎn)亂。。。所以不要在意細(xì)節(jié)了。。...

    AbnerMing 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<