HTTP協(xié)議小結(jié)

寫(xiě)在前面

本文是閱讀了《圖解HTTP》一文同時(shí)本周也在一個(gè)小團(tuán)體內(nèi)進(jìn)行了一些HTTP協(xié)議的討論,感覺(jué)對(duì)于HTTP協(xié)議的理解比以前又加深了不少。

歷史

以下材料選自《圖解HTTP》:

在學(xué)習(xí)Http協(xié)議之前首先看一下他的歷史,也許能解答我們不少的問(wèn)題。

1989年3月,互聯(lián)網(wǎng)還只屬于少數(shù)人。在這一互聯(lián)網(wǎng)的黎明期,HTTP誕生了。CERN(歐洲核子研究組織)的蒂姆·伯納斯-李(Tim BernersLee)博士提出了一種能讓遠(yuǎn)隔兩地的研究者們共享知識(shí)的設(shè)想。

最初設(shè)想的基本理念是:借助多文檔之間的相互關(guān)聯(lián)形成的超文本(Hyper Text),連成可相互參閱的WWW(World Wide Web,萬(wàn)維網(wǎng))。WWW這一名稱(chēng),是Web瀏覽器用來(lái)瀏覽超文本的客戶(hù)端應(yīng)用程序時(shí)的名稱(chēng)?,F(xiàn)在則用來(lái)表示這一系列的集合,也可簡(jiǎn)稱(chēng)為Web

  • HTTP/0.9
    HTTP于1990年問(wèn)世。那時(shí)的HTTP并沒(méi)有作為正式的標(biāo)準(zhǔn)被建立?,F(xiàn)在的HTTP其實(shí)含有HTTP1.0之前版本的意思,因此被稱(chēng)為HTTP/0.9。

  • HTTP/1.0
    HTTP正式作為標(biāo)準(zhǔn)被公布是在1996年的5月,版本被命名為HTTP/1.0,并記載于RFC1945。雖說(shuō)是初期標(biāo)準(zhǔn),但該協(xié)議標(biāo)準(zhǔn)至今仍被廣泛使用在服務(wù)器端。

  • HTTP/1.1
    1997年1月公布的HTTP/1.1是目前主流的HTTP協(xié)議版本。當(dāng)初的標(biāo)準(zhǔn)是RFC2068,之后發(fā)布的修訂版RFC2616就是當(dāng)前的最新版本。

HTTP協(xié)議的根基

在了解HTTP協(xié)議之前,首先對(duì)整個(gè)計(jì)算機(jī)網(wǎng)絡(luò)協(xié)議做一個(gè)大致的了解。首先要提到的是ISO指定的OSI七層模型,但是這個(gè)參考模型過(guò)于龐大、復(fù)雜。與此相對(duì)的是技術(shù)人員自己開(kāi)發(fā)的TCP/IP協(xié)議族獲得了更廣泛的應(yīng)用。

TCP/IP參考模型

對(duì)于我們應(yīng)用端程序員來(lái)說(shuō),接觸最多的還是應(yīng)用層的協(xié)議,HTTP協(xié)議也是屬于這一層的。但是HTTP協(xié)議是基于傳輸層協(xié)議的,傳輸層的協(xié)議是TCP和UDP協(xié)議,使用TCP協(xié)議連接會(huì)經(jīng)歷三次握手,而UDP不會(huì)。HTTP協(xié)議是基于可靠的TCP協(xié)議的,關(guān)于TCP/IP我們需要了解的東西暫時(shí)就到這了。更多的可以自己去看《TCP/IP詳解 卷1》。最初HTTP協(xié)議的出現(xiàn)時(shí)為了解決WEB間數(shù)據(jù)問(wèn)題的,時(shí)至今日,因?yàn)樗?jiǎn)單的特性已經(jīng)被用于各種場(chǎng)景了(比如咱Android和IOS,雖然以后可能會(huì)被https取代。。),所以在具體了解HTTP協(xié)議的時(shí)候,你可能會(huì)發(fā)現(xiàn)很多東西是你平時(shí)用不上的,這都是很正常的。

HTTP首部

HTTP協(xié)議的請(qǐng)求響應(yīng)報(bào)文中必定包含HTTP首部,只是我們平時(shí)在使用Web的過(guò)程中感受不到它。
HTTP報(bào)文的結(jié)構(gòu):
報(bào)文首部 + 空行 + 報(bào)文主體

HTTP請(qǐng)求報(bào)文
在請(qǐng)求中,HTTP報(bào)文由方法、URI、HTTP版本、HTTP首部字段等部分構(gòu)成。
報(bào)文首部構(gòu)成:請(qǐng)求行 + 請(qǐng)求首部字段 + 通用首部字段 + 實(shí)體首部字段 + 其他
其中:請(qǐng)求首部字段+通用首部字段+實(shí)體首部字段就是HTTP首部字段。

讓我們通過(guò)一個(gè)簡(jiǎn)單的請(qǐng)求看一下:

簡(jiǎn)書(shū)

首先我在瀏覽器輸入的=>jianshu.com
上面的General可以看到一個(gè)Status Code:301,這個(gè)狀態(tài)碼代表的意思就是永久性重定向。該狀態(tài)碼表示請(qǐng)求的資源已經(jīng)被分配了新的URI,希望用戶(hù)(本次)能使用新的URI訪(fǎng)問(wèn),那為啥呢?因?yàn)槲逸數(shù)氖?a target="_blank">jianshu.com,但是實(shí)際上簡(jiǎn)書(shū)的域名是www.itdecent.cn

另外在HTTP協(xié)議中狀態(tài)碼是一個(gè)比較重要的東西,我們通常需要根據(jù)狀態(tài)碼來(lái)得到一些基本的信息,比如說(shuō)本次請(qǐng)求是否成功。那么簡(jiǎn)單的羅列一下HTTP協(xié)議中的一些狀態(tài)碼:

1XX 信息性狀態(tài)碼 接收的請(qǐng)求正在處理
2XX 成功狀態(tài)碼 請(qǐng)求正常處理完畢
3XX 重定向狀態(tài)碼 需要進(jìn)行附加操作已完成請(qǐng)求
4XX 客戶(hù)端錯(cuò)誤狀態(tài)碼 服務(wù)器無(wú)法處理請(qǐng)求
5XX 服務(wù)器錯(cuò)誤狀態(tài) 服務(wù)器處理請(qǐng)求出錯(cuò)

以上是對(duì)狀態(tài)碼的一些簡(jiǎn)介,如果你想要了解更詳細(xì)的,可以自行g(shù)oogle。

HTTP首部字段結(jié)構(gòu)
HTTP首部字段是由首部字段名和字段值構(gòu)成的,中間用冒號(hào)“:”分割。

首部字段名:字段值
例如,在HTTP首部中以Content-Type這個(gè)字段來(lái)表示報(bào)文主體的對(duì)象類(lèi)型。
Content-Type:text/html
就以上述示例來(lái)看,首部字段名為Content-Type,字符串text/html是字段值。

另外,字段值對(duì)應(yīng)單個(gè)HTTP首部字段可以有多個(gè)值,入下所示
Keep-Alive:timeout=15,max=100

HTTP
HTTP
HTTP
HTTP

除了以上HTTP/1.1協(xié)議(RFC2616)中規(guī)定的47種首部字段,還有一些非正式的首部字段,這些字段被統(tǒng)一歸納在RFC4229 HTTP Header Field Registrations中。常用的Cookie、SetCookie和Content-Disposition等。

看了以上那么多首部字段,是不是有些眼花繚亂?其實(shí)根本沒(méi)有那么復(fù)雜……對(duì)于我們android開(kāi)發(fā)者來(lái)說(shuō),在平時(shí)的應(yīng)用場(chǎng)景中只要知道Content-Type和Cookie就能應(yīng)付大部分場(chǎng)景了。首先Cookie這玩意一般是用來(lái)驗(yàn)證用戶(hù)身份的。眾所周知HTTP協(xié)議是一個(gè)無(wú)狀態(tài)協(xié)議,是不能記住用戶(hù)的。Cookie應(yīng)運(yùn)而生,而在現(xiàn)在,一般來(lái)說(shuō)在服務(wù)器端會(huì)將用戶(hù)記錄入一個(gè)session,而在你登錄的請(qǐng)求中,服務(wù)器會(huì)返回一個(gè)cookie,這時(shí)候就需要我們?nèi)ツ玫竭@個(gè)cookie并在每一次請(qǐng)求中都加入了。通常這個(gè)cookie是一個(gè)session id,當(dāng)然了,如果你們服務(wù)端的大兄弟返一個(gè)token給你,讓你加在請(qǐng)求頭里,那也隨他開(kāi)心,他要咱怎么做,咱就配合就成了。Content-Type是指明請(qǐng)求實(shí)體的媒體類(lèi)型,具體的值可以在用到的時(shí)候自行去查找。

簡(jiǎn)單的請(qǐng)求

接下來(lái)使用Okhttp來(lái)進(jìn)行一些簡(jiǎn)單的請(qǐng)求:

        //GET請(qǐng)求
        Request getRequest = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = mOkhttpClient.newCall(getRequest);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println(response.body().string());
            }
        });

首先這是一個(gè)最簡(jiǎn)單的GET請(qǐng)求,首先創(chuàng)建一個(gè)請(qǐng)求Request,再創(chuàng)建一個(gè)Call,最后將這個(gè)請(qǐng)求調(diào)度這個(gè)call,最終你可以在onResponse這個(gè)回調(diào)里處理響應(yīng)報(bào)文。這里有一個(gè)小地方值得注意,不要重復(fù)使用response.body().string(),當(dāng)然如果你有需要,可以先將他賦給一個(gè)String的引用,不然第二次使用它的時(shí)候可能為空。這一點(diǎn)在我使用的時(shí)候還是這樣,各位看官對(duì)于為什么這樣感興趣的話(huà)可以去搜搜。

再來(lái)一個(gè)普通的表單請(qǐng)求:

        //表單請(qǐng)求
        FormBody formBody = new FormBody.Builder()
                .add("name","18255697137")
                .add("pass","123")
                .build();

        Request postFormRequest = new Request.Builder()
                .url(...)
                .post(formBody)
                .build();

        Call postFormCall = mOkhttpClient.newCall(postFormRequest);
        postFormCall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String str = response.body().string();
                System.out.println(str);
                testText.setText(str);
            }
        });

    }

與上面的GET請(qǐng)求對(duì)比,可以看出多了構(gòu)建請(qǐng)求實(shí)體的步驟,這里我模擬登陸的場(chǎng)景,提交明文的賬號(hào)密碼。先說(shuō)說(shuō)我對(duì)于這種構(gòu)建請(qǐng)求的看法:

  • 步驟很清晰,很HTTP
  • 用起來(lái)著實(shí)麻煩

對(duì)于Okhttp進(jìn)行封裝的庫(kù)我用過(guò)兩個(gè),一個(gè)是OkhttpUtils,是鴻洋大神的作品,另一個(gè)就是Square的Retrofit了。OkhttpUtils好用是挺好用的,鏈?zhǔn)絘pi,構(gòu)建一個(gè)HTTP請(qǐng)求非常簡(jiǎn)單,但是對(duì)于RxJava和響應(yīng)與實(shí)體轉(zhuǎn)換的支持并不是很好,所以我個(gè)人更傾向于Retrofit。

那么用Retrofit+RxJava構(gòu)建一個(gè)表單請(qǐng)求會(huì)是怎樣的呢?

首先定義一個(gè)ApiStore接口,里面包含了一個(gè)register方法

public interface ApiStore {
    /**
     * 注冊(cè)
     */
    @FormUrlEncoded
    @POST("api.php?action=register")
    Observable<BaseResponse> register(
            @Field("name")String username
            ,@Field("pass")String password);
}

     

定義一個(gè)接收數(shù)據(jù)的類(lèi)型:

public class BaseResponse {
    int code;
    String message;

    @Override
    public String toString() {
        return "the code is--->" + code + "\n" +
                "the message is--->" + message;
    }
}

發(fā)送請(qǐng)求:

        TextView testText = (TextView) findViewById(R.id.test);
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASEURL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();


        ApiStore apiStore = retrofit.create(ApiStore.class);
        apiStore.register("182*******7","123")
                .subscribeOn(Schedulers.io())
                .map(BaseResponse::toString)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(testText::setText,Throwable::printStackTrace);

效果圖:

效果圖

可能你看了我對(duì)于Retrofit的使用,感覺(jué)這不比Okhttp的使用還要麻煩么。。。其實(shí)不是的,一是現(xiàn)在演示的都是很基礎(chǔ)簡(jiǎn)單的東西,一是我也是剛上手Retrofit,應(yīng)該還可以進(jìn)一步的封裝。

關(guān)于Okhttp的使用暫時(shí)就到這,本文并非專(zhuān)門(mén)總結(jié)Okhttp使用方法的,只是順帶一提。今天在討論完HTTP的一些東西之后,突然又聊了一些HTTP安全方面的東西。眾所周知,HTTP是明文傳輸?shù)?,因?yàn)樵谝郧巴ǔU?qǐng)求的都是一些資源文件,沒(méi)有什么加密的必要。但是現(xiàn)在用在移動(dòng)客戶(hù)端,這還是挺要命的。有的小伙伴就說(shuō)了,他們那用MD5……

首先我們想清楚MD5是干什么的,MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于確保信息傳輸完整一致。MD5并不是一個(gè)加密算法,所以你用MD5處理數(shù)據(jù)傳到服務(wù)端是沒(méi)什么卵用的,只不過(guò)是把人能看懂的字符弄成人理解不了的字符串而已。這有啥,我模擬你登錄的時(shí)候,我直接把你MD5處理過(guò)的字符串加上來(lái)不就成了,你服務(wù)器認(rèn)這個(gè)不就OK?

所以在傳輸重要數(shù)據(jù)時(shí)可以考慮使用https或者非對(duì)稱(chēng)加密,為毛是非對(duì)稱(chēng)加密呢。。。因?yàn)槿绻菍?duì)稱(chēng)加密,那么客戶(hù)端會(huì)留有一個(gè)公鑰,這有點(diǎn)不靠譜。。??蛻?hù)端的安全保障。。。當(dāng)然你可以把密鑰放到so里面,但是我沒(méi)做過(guò),不知道這樣到底安不安全。。。

接下來(lái)是關(guān)于GET和POST這兩種請(qǐng)求的區(qū)別:
來(lái)自WebTechGarden

  • GET請(qǐng)求在URL中傳送的參數(shù)是有長(zhǎng)度限制的,而POST沒(méi)有。
  • GET參數(shù)通過(guò)URL傳遞,POST放在Request body中
  • GET比POST更不安全,因?yàn)閰?shù)直接暴露在URL上,所以不能用來(lái)傳遞敏感信息。

以上的回答可能是你見(jiàn)的最多的答案了,可是那篇文的作者給出了從另一個(gè)角度得出的結(jié)論:GET和POST本質(zhì)上沒(méi)有區(qū)別。
GET和POST是HTTP協(xié)議中兩種發(fā)送請(qǐng)求的方法,而HTTP協(xié)議是基于TCP/IP關(guān)于數(shù)據(jù)如何在萬(wàn)維網(wǎng)中如何通信的協(xié)議。如果你要給GET加上request body,給POST帶上url參數(shù),技術(shù)上是完全行得通的。

更多的內(nèi)容自行戳上面的鏈接,我這只是給個(gè)引子~

本次的HTTP小結(jié)就暫時(shí)告一段落~

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本篇文章篇幅比較長(zhǎng),先來(lái)個(gè)思維導(dǎo)圖預(yù)覽一下。 一、概述 1.計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)分層 2.TCP/IP 通信傳輸流 ...
    滌生_Woo閱讀 56,199評(píng)論 24 557
  • 1. 網(wǎng)絡(luò)基礎(chǔ)TCP/IP HTTP基于TCP/IP協(xié)議族,HTTP屬于它內(nèi)部的一個(gè)子集。 把互聯(lián)網(wǎng)相關(guān)聯(lián)的協(xié)議集...
    yozosann閱讀 3,605評(píng)論 0 20
  • 前言 本系列主要分析OKHttp源代碼的框架和設(shè)計(jì)思想,因?yàn)镺KHttp實(shí)現(xiàn)了HTTP協(xié)議,所以在做源代碼分析之前...
    嘎啦果安卓獸閱讀 4,288評(píng)論 1 15
  • http協(xié)議是指計(jì)算機(jī)通信網(wǎng)絡(luò)中兩臺(tái)計(jì)算機(jī)之間進(jìn)行通信所必須共同的規(guī)定和規(guī)則,超文本傳輸協(xié)議(http)是一種“通...
    劉巍l閱讀 237評(píng)論 0 0
  • 本文是《圖解HTTP》讀書(shū)筆記的第二篇,主要包括此書(shū)的第六章內(nèi)容,因?yàn)榈诹碌膬?nèi)容較多,而且比較重要,所以單獨(dú)寫(xiě)為...
    lijiankun24閱讀 1,498評(píng)論 0 6

友情鏈接更多精彩內(nèi)容