HTTP協(xié)議詳解與Android相關(guān)基礎(chǔ)網(wǎng)絡(luò)編程

大量基礎(chǔ)知識預(yù)警,大神請繞道~~

[TOC]
#### 一.從HTML說起

一.從HTML說起

HTML是Hyper Text Mark-up Language 的縮寫,即“超文本標(biāo)記語言”。注意這幾個字——“超文本”是指頁面內(nèi)可以包含圖片、鏈接、甚至音樂、程序等非文字元素"標(biāo)記語言"——是一種將文本(Text)以及文本相關(guān)的其他信息結(jié)合起來,展現(xiàn)出關(guān)于文檔結(jié)構(gòu)和數(shù)據(jù)處理細節(jié)的計算機文字編碼(維基百科),這個定義難理解的話想想我們熟悉的東西——我們做Android開發(fā)時寫的xml文件就是標(biāo)記語言,XML全稱為可擴展標(biāo)記語言(Extensible Markup Language,簡稱XML)。
??作為個人或者組織在萬維網(wǎng)(web)上放置的開始頁面稱為“主頁”,就像這張:

主頁.png

主頁中有指向其他相關(guān)頁面或其他節(jié)點的指針(超鏈接)。所謂超鏈接就是一種統(tǒng)一資源定位器指針(Uniform Resource Locator),也就是傳說中的URL(更通俗一點的說就是“網(wǎng)址”),通過點擊激活它,可使瀏覽器方便快捷的訪問其他的網(wǎng)頁。嗯,這就是一排URL:

URL.png

設(shè)計HTML 語言的目的是為了能把存放在一臺電腦中的文本或圖形與另一臺電腦中的文本或圖形方便地聯(lián)系在一起,形成有機的整體,人們不用考慮具體信息是在當(dāng)前電腦上還是在網(wǎng)絡(luò)的其它電腦上。這樣,你只要使用鼠標(biāo)在某一文檔中點取一個圖標(biāo),Internet就會馬上轉(zhuǎn)到與此圖標(biāo)相關(guān)的內(nèi)容上去,而這些信息可能存放在網(wǎng)絡(luò)的另一臺電腦中.

二.什么是HTTP

1.什么是HTTP協(xié)議?

協(xié)議是指計算機通信網(wǎng)絡(luò)中兩臺計算機之間進行通信的所必需共同遵守的規(guī)則。超文本傳輸協(xié)議(HTTP)是一種基于TCP鏈接的通信協(xié)議,是一個客戶端和服務(wù)端請求和應(yīng)答的標(biāo)準(zhǔn),它允許將超文本標(biāo)記語言(HTML)文檔從web服務(wù)器傳到客戶端的瀏覽器。
??我們想瀏覽一個網(wǎng)站的時候,只要在瀏覽器的地址欄里輸入網(wǎng)站的地址就可以了,例如www.baidu.com,但是在瀏覽器的地址欄里面出現(xiàn)的卻是:http://www.baidu.com ,你知道為什么會多出一個“http”嗎?
??我們在瀏覽器的地址欄里輸入的網(wǎng)站地址也就是URL。就像每家每戶都有一個門牌地址一樣,每個網(wǎng)頁也都有一個Internet地址。當(dāng)你在瀏覽器的地址框中輸入一個URL或是單擊一個超級鏈接時,URL就確定了要瀏覽的地址。瀏覽器通過超文本傳輸協(xié)議(HTTP),將Web服務(wù)器上站點的網(wǎng)頁代碼提取出來,并翻譯成漂亮的網(wǎng)頁。因此,在我們認(rèn)識HTTP之前,有必要先弄清楚URL的組成。

2.web服務(wù)器、瀏覽器、代理服務(wù)器

當(dāng)我們打開瀏覽器,在地址欄中輸入URL,跳轉(zhuǎn)就會看到相應(yīng)的網(wǎng)頁。實際上當(dāng)我們輸入URL并敲擊回車之后,我們的瀏覽器給web服務(wù)器發(fā)送了一個Request,web服務(wù)器接到這個Request之后做出相應(yīng)的處理,生成相應(yīng)的Response,然后發(fā)送給瀏覽器,瀏覽器解析Response中的HTML,我們就看到了網(wǎng)頁:

圖片1.png

我們的Request有可能是經(jīng)過了代理服務(wù)器的,最后才抵達Web服務(wù)器:

圖片2.png

代理服務(wù)器(Proxy Server)就是網(wǎng)絡(luò)信息的中轉(zhuǎn)站,其功能就是代理網(wǎng)絡(luò)用戶去獲得網(wǎng)絡(luò)信息,有了它瀏覽器不是直接到web服務(wù)器去取回網(wǎng)頁,而是向代理服務(wù)器發(fā)出Request,由他取回瀏覽器所需要的信息并傳遞給你的瀏覽器。他有以下幾種功能:
①提高訪問速度
??大多數(shù)代理服務(wù)器都有緩存功能
②突破網(wǎng)絡(luò)限制
??比如局域網(wǎng)對上網(wǎng)用戶的端口、目的網(wǎng)站、協(xié)議、游戲、即時通訊軟件等的限制,也就是翻墻了。比如A要訪問C網(wǎng)站,但A到C網(wǎng)絡(luò)出現(xiàn)問題,可以通過繞道,假設(shè)B是代理服務(wù)器,A可通過B, 再由B到C。
③隱藏自己的真實地址、身份信息
??隱藏自己的IP,防止被黑客攻擊。通過分析指定IP地址,可以查詢到網(wǎng)絡(luò)用戶的目前所在地。代理服務(wù)器知識是黑客基本功 ,黑客的很多活動都是通過代理服務(wù)器, 比如掃描、刺探,對局域網(wǎng)內(nèi)機器進行滲透,黑客一般攻擊的時候都是中轉(zhuǎn)了很多級跳板,才攻擊目標(biāo)機器。隱藏了身份,保證了自己的安全。

3.URL詳解

超鏈接URL地址用于描述一個網(wǎng)絡(luò)上的資源,其基本格式如下:

schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]
scheme             指定低層使用的協(xié)議(例如:http, https, ftp)
host                  HTTP服務(wù)器的IP地址或者域名
port#                 HTTP服務(wù)器的默認(rèn)端口是80,這種情況下端口號可以省略。如果使用了別的端口,必須指明,例如 http://www.cnblogs.com:8080/
path                  訪問資源的路徑
url-params          Url請求參數(shù)
query-string          發(fā)送給http服務(wù)器的數(shù)據(jù)
anchor-               錨

舉個栗子(這個網(wǎng)址不知道當(dāng)時是在哪找的,現(xiàn)在已經(jīng)404了):

http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

從上面的URL可以看出,一個完整的URL包括以下幾部分:
(1)協(xié)議部分(scheme):該URL的協(xié)議部分為“http:”這代表網(wǎng)頁使用的是HTTP協(xié)議。在Internet中有多種協(xié)議,如HTTP,F(xiàn)TP等等本例中使用的是HTTP協(xié)議。“//”為分隔符。
(2)域名部分(host):該URL的域名部分為“www.aspxfans.com”,通知aspxfans.com服務(wù)器顯示web。一個URL中,也可以使用IP地址作為域名使用。
(3)端口部分(port#):跟在域名后面的是端口,域名和端口之間使用“:”作為分隔符。端口不是一個URL必須的部分,如果省略端口部分,將采用默認(rèn)端口(80),此處的端口號是8080。
(4)虛擬目錄部分(path訪問資源的路徑):從域名后的第一個“/”開始到最后一個“/”為止,是虛擬目錄部分。虛擬目錄也不是一個URL必須的部分。本例中的虛擬目錄是“/news/”,為該服務(wù)器上的子目錄。可以這么理解,域名對應(yīng)的服務(wù)器就是C盤,虛擬目錄就是Program Files等子文件夾。
(5)文件名部分(url-params):從域名后的最后一個“/”開始到“?”為止,是文件名部分,如果沒有“?”,則是從域名后的最后一個“/”開始到“#”為止,是文件部分,如果沒有“?”和“#”,那么從域名后的最后一個“/”開始到結(jié)束,都是文件名部分。
??本例中的文件名是“index.asp”,是我們要取的“/news/”文件夾中的一個HTML網(wǎng)頁。文件名部分也不是一個URL必須的部分,如果省略該部分,則使用默認(rèn)的文件名。
(6)參數(shù)部分(query-string)從“?”開始到“#”為止之間的部分為參數(shù)部分,又稱搜索部分、查詢部分。本例中的參數(shù)部分為“boardID=5&ID=24618&page=1”。參數(shù)可以允許有多個參數(shù),參數(shù)與參數(shù)之間用“&”作為分隔符
(7)錨部分(anchor):從“#”開始到最后,都是錨部分。本例中的錨部分是“name”。錨部分也不是一個URL必須的部分。
??以上例子參考博客:http://blog.csdn.net/ergouge/article/details/8185219,我們可以分析一下這個博客的url:
該URL采用http協(xié)議,域名為blog.csdn.net,path資源路徑(虛擬目錄)為/ergouge/article/details/,這個類似于C:\Users\dell.android\avd一層一層的文件夾,而我們要獲取的的“文件”(HTML網(wǎng)頁)id即為8185219。

4.Http協(xié)議工作原理

Web瀏覽器和Web服務(wù)器之間是如何建立連接的呢?主要是通過以下四個步驟實現(xiàn)的。
??第一步,在客戶端的瀏覽器中獲取用戶的輸入內(nèi)容。
??第二步,瀏覽器得到網(wǎng)址后,會將域名發(fā)送到DNS服務(wù)器上,進行域名解析,得到目的服務(wù)器的IP地址。
??第三步,使用Socket套接字來實現(xiàn)TCP/IP鏈接。從瀏覽器到服務(wù)器端口使用的是TCP/IP協(xié)議(網(wǎng)絡(luò)層)來完成的。
??第四步,服務(wù)器的80端口監(jiān)聽客戶端的鏈接,完成客戶端到服務(wù)器的連接。
??上述四個步驟的具體實現(xiàn)過程如圖所示。而在Internet內(nèi)部可以通過三種方式來實現(xiàn)發(fā)送和接收數(shù)據(jù),分別是Http協(xié)議、FTP協(xié)議和TCP/IP協(xié)議。

圖片3.png

服務(wù)器返回客戶端的內(nèi)容有三種形式,分別是:
??(1)以Html代碼的形式返回。
??(2)以xml字符串的形式返回。
??(3)以Json數(shù)據(jù)形式返回,從網(wǎng)絡(luò)流量的角度考慮,Json方式要比xml方式好一些,且便于解析。

三.HTTP的報文結(jié)構(gòu)

HTTP協(xié)議采用了請求/響應(yīng)模型。客戶端向服務(wù)器發(fā)送一個請求,請求頭包含請求的方法、URL、協(xié)議版本、以及包含請求修飾符、客戶信息和內(nèi)容的消息結(jié)構(gòu)。服務(wù)器以一個狀態(tài)行作為響應(yīng),相應(yīng)的內(nèi)容包括消息協(xié)議的版本,成功或者錯誤編碼加上包含服務(wù)器信息、實體元信息以及可能的實體內(nèi)容
??通常HTTP消息包括客戶機向服務(wù)器的請求消息(Request)服務(wù)器向客戶機的響應(yīng)消息(Respone)。這兩種類型的消息由一個起始行,一個或者多個頭域,一個指示頭域結(jié)束的空行和可選的消息體組成。HTTP的頭域包括通用頭,請求頭,響應(yīng)頭和實體頭四個部分。每個頭域由一個域名,冒號(:)和域值三部分組成。

1.通用頭域

通用頭域包含請求和響應(yīng)消息都支持/包含的頭域,通用頭域包含Cache-Control、Connection、Date、Content-Type、Content-Transfer-Encoding等。對通用頭域的擴展要求通訊雙方都支持此擴展,如果存在不支持的通用頭域,一般將會作為實體頭域處理。下面簡單介紹幾個經(jīng)常使用的通用頭域。
①Cache-Control頭域:
??Cache-Control指定請求和響應(yīng)遵循的緩存機制。在請求消息或響應(yīng)消息中設(shè)置Cache-Control并不會修改另一個消息處理過程中的緩存處理過程。
??請求時的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached。
??響應(yīng)消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
各個消息中的指令含義如下:

Public:指示響應(yīng)可被任何緩存區(qū)緩存。
Private:指示對于單個用戶的整個或部分響應(yīng)消息,不能被共享緩存處理。這允許服務(wù)器僅僅描述當(dāng)用戶的部分響應(yīng)消息,此響應(yīng)消息對于其他用戶的請求無效。
no-cache:指示請求或響應(yīng)消息不能緩存 。
no-store:用于防止重要的信息被無意的發(fā)布。在請求消息中發(fā)送將使得請求和響應(yīng)消息都不使用緩存。
max-age:指示客戶機可以接收生存期不大于指定時間(以秒為單位)的響應(yīng)。
min-fresh:指示客戶機可以接收響應(yīng)時間小于當(dāng)前時間加上指定時間的響應(yīng)。
max-stale:指示客戶機可以接收超出超時期間的響應(yīng)消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內(nèi)的響應(yīng)消息。

②HTTP Keep-Alive:
??Keep-Alive功能使客戶端到服務(wù)器端的連接持續(xù)有效,當(dāng)出現(xiàn)對服務(wù)器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。對于提供靜態(tài)內(nèi)容的網(wǎng)站來說,這個功能通常很有用。
??但是,對于負擔(dān)較重的網(wǎng)站來說,這里存在另外一個問題:雖然為客戶保留打開的連接有一定的好處,但它同樣影響了性能,因為在處理暫停期間,本來可以釋放的資源仍舊被占用。當(dāng)Web服務(wù)器和應(yīng)用服務(wù)器在同一臺機器上運行時,Keep- Alive功能對資源利用的影響尤其突出。
③Date頭域:
??Date頭域表示消息發(fā)送的時間,時間的描述格式由rfc822定義。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的時間表示世界標(biāo)準(zhǔn)時,換算成本地時間,需要知道用戶所在的時區(qū)。
④Pragma頭域:
??Pragma頭域用來包含實現(xiàn)特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1協(xié)議中,它的含義和Cache-Control:no-cache相同。

2.Request 請求消息(RequestMessage)

先看Request 消息的結(jié)構(gòu),Request 消息分為3部分,第一部分叫請求行,第二部分叫http header, 第三部分是body。header和body之間有個空行,結(jié)構(gòu)如下圖:

Request 請求報文.png

如圖,我們可以看到,一個完整的Request請求包括三部分——Frist line: GET/POST [URL路徑] HTTP/[HTTP版本]+Header請求頭域+Request Body。下面我們來一一講解講解這三個重要部分。
(1)請求行:GET/POST [URL路徑] HTTP/[HTTP版本]
??Http協(xié)議定義了很多與服務(wù)器交互的方法,最基本的有4種:GET,POST,PUT,DELETE。 一個URL地址用于描述一個網(wǎng)絡(luò)上的資源,而HTTP中的GET, POST, PUT, DELETE就對應(yīng)著對這個資源的查,改,增,刪4個操作。 我們最常見的就是GETPOST了。
??GET一般用于取回由Request-URI標(biāo)識的信息(獲取/查詢資源信息)。
??POST一般用于更新資源信息(這里的更新值得是跟原來不一樣,準(zhǔn)確的說是新增。PUT才是嚴(yán)格意義上的修改/跟新),請求服務(wù)器接收包含在請求中的實體信息。可以用于提交表單,向新聞組、BBS、郵件群組和數(shù)據(jù)庫發(fā)送消息。
我們看看GET和POST的區(qū)別:
??①GET提交的數(shù)據(jù)會放在URL之后,以“?”分割URL和傳輸數(shù)據(jù),參數(shù)之間以“&”相連,如EditPosts.aspx?name=test1&id=123456;POST方法是把提交的數(shù)據(jù)放在HTTP數(shù)據(jù)包的Body中
??②GET提交的數(shù)據(jù)就在URL中,所以大小有限制(因為瀏覽器對URL的長度有限制);POST方法提交的數(shù)據(jù)沒有限制。
??③GET方式需要使用Request.QueryString來取得變量的值;POST方式通過Request.Form來獲取變量的值。
??④GET方式提交數(shù)據(jù),會帶來安全問題,比如一個登錄頁面,通過GET方式提交數(shù)據(jù)時,用戶名和密碼將出現(xiàn)在URL上,如果頁面可以被緩存或者其他人可以訪問這臺機器,就可以從歷史記錄獲得該用戶的賬號和密碼。
(2)Header請求頭域
??請求頭域允許客戶端向服務(wù)器傳遞關(guān)于客戶端的附加信息,請求頭域可能包含下列字段(筆者就整理了這么多,歡迎補充):

1)Accept:設(shè)置服務(wù)器返回的數(shù)據(jù)類型。
2)Accept-Languge:設(shè)置服務(wù)器返回的語言。
3)Accept-Encoding:設(shè)置服務(wù)器返回的壓縮編碼。
4)Accept-Charset:設(shè)置服務(wù)器返回的文字編碼。
5)User-Agent:請求類型唯一標(biāo)識。
6)Host頭域 : Host頭域指定請求資源的Intenet主機和端口號,必須表示請求url的原始服務(wù)器或網(wǎng)關(guān)的位置。HTTP/1.1請求必須
    包含主機頭域,否則系統(tǒng)會以400狀態(tài)碼返回。
7)Referer頭域 : Referer頭域允許客戶端指定請求uri的源資源地址,這可以允許服務(wù)器生成回退鏈表,可用來登陸、優(yōu)化cache
    等。他也允許廢除的或錯誤的連接由于維護的目的被追蹤。如果請求的uri沒有自己的uri地址,Referer不能被發(fā)送。如果指
    定的是部分URL地址,則此地址應(yīng)該是一個相對地址。
8)User-Agent頭域:User-Agent頭域的內(nèi)容包含發(fā)出請求的用戶信息。
9)Range頭域: Range頭域可以請求實體的一個或者多個子范圍。
        例如, 表示頭500個字節(jié):bytes=0-499
        表示第二個500字節(jié):bytes=500-999
        表示最后500個字節(jié):bytes=-500
        表示500字節(jié)以后的范圍:bytes=500-
        第一個和最后一個字節(jié):bytes=0-0,-1
        同時指定幾個范圍:bytes=500-600,601-999
        但是服務(wù)器可以忽略此請求頭,如果無條件GET包含Range請求頭,響應(yīng)會以狀態(tài)碼206(PartialContent)返回而不是200(OK)。

(3)Request Body(只有POST請求的Body有意義)
??注意是POST,如果是GET的話這部分是空的,因為GET只是單純的請求獲取一些東西,他的參數(shù)都在URL中的那個"?"后面標(biāo)明了;而POST則是要往服務(wù)器上發(fā)送一些東西(用于數(shù)據(jù)更新等),這個body就是我們發(fā)送的東西

3.Response應(yīng)答消息

我們再看Response消息的結(jié)構(gòu), 和Request消息的結(jié)構(gòu)基本一樣。 同樣也分為三部分,第一部分叫request line(請求行), 第二部分叫request header(請求頭),第三部分是Respone body。 header和body之間也有個空行,結(jié)構(gòu)如下圖:

Respone報文格式.png

Response 消息中的第一行叫做狀態(tài)行,由HTTP協(xié)議版本號,狀態(tài)碼,狀態(tài)消息三部分組成。狀態(tài)碼和狀態(tài)信息也就是上面表格中的200 OK,HTTP/1.1中有5類狀態(tài)碼,狀態(tài)碼由三位數(shù)字組成,第一個數(shù)字定義了響應(yīng)的類別:

    1XX  提示信息 - 表示請求已被成功接收,繼續(xù)處理
  2XX  成功 - 表示請求已被成功接收,理解,接受
  3XX  重定向 - 要完成請求必須進行更進一步的處理
  4XX  客戶端錯誤 -  請求有語法錯誤或請求無法實現(xiàn)
  5XX  服務(wù)器端錯誤 -   服務(wù)器未能實現(xiàn)合法的請求
  
看看一些常見的狀態(tài)碼:
    200 OK:最常見的就是成功響應(yīng)狀態(tài)碼200了, 這表明該請求被成功地完成,所請求的資源發(fā)送回客戶端。
    302 Found:重定向,新的URL會在response中的Location中返回,瀏覽器將會使用新的URL發(fā)出新的Request。
        例如在IE中輸入http://www.google.com. HTTP服務(wù)器會返回304, IE取到Response中Location header的新URL, 又重新發(fā)送了一個Request.
    304 Not Modified:代表上次的文檔已經(jīng)被緩存了, 還可以繼續(xù)使用,
        提示:如果你不想使用本地緩存可以用Ctrl+F5強制刷新頁面
    400 Bad Request  客戶端請求與語法錯誤,不能被服務(wù)器所理解
    403 Forbidden  服務(wù)器收到請求,但是拒絕提供服務(wù)
    404 Not Found  請求資源不存在(輸錯了URL)

這里我們再解釋一下Response消息中的請求頭中幾個常見參數(shù):

1)Content-Type:是HTTP協(xié)議header中一個重要的參數(shù),它用于標(biāo)識發(fā)送或接收到的數(shù)據(jù)的類型,缺省值為" text/plain"。瀏
    覽器根據(jù)該參數(shù)來決定數(shù)據(jù)的打開方式。Content-Type使用的是 “主類型/子類型; 額外參數(shù)” ([type]/[subtype]; 
    parameter )的數(shù)值格式。主要類型有9種,分別是application、audio、example、image、message、model、
    multipart、text、video。
            “子類型” (subtype)用于指定"主類型"的詳細形式。 其中以x-開頭表示該類別尚未標(biāo)準(zhǔn)化。當(dāng)客戶端不能確定“子
    類型”時,會根據(jù)“主類型”來獲取默認(rèn)的子類型。 "額外參數(shù)" (parameter)用于指定請求/響應(yīng)內(nèi)容的字符編碼格式。例如
    text/html;charset=utf-8;
        ①text:用于標(biāo)準(zhǔn)化地表示的文本信息,文本消息可以是多種字符集和或者多種格式的;默認(rèn)是text/plain; 
            text/plain:純文本,文件擴展名.txt
            text/html:HTML文本,文件擴展名.htm和.html
        ②multipart:用于連接消息體的多個部分構(gòu)成一個消息,這些部分可以是不同類型的數(shù)據(jù); 默認(rèn)是multipart/mixed;
        ③image:用于傳輸靜態(tài)圖片數(shù)據(jù);
            image/jpeg:jpeg格式的圖片,文件擴展名.jpg
            image/gif:GIF格式的圖片,文件擴展名.gif
        ④audio:用于傳輸音頻或者音聲數(shù)據(jù);
            audio/x-wave:WAVE格式的音頻,文件擴展名.wav
            audio/mpeg:MP3格式的音頻,文件擴展名.mp3
        ⑤video:用于傳輸動態(tài)影像數(shù)據(jù),可以是與音頻編輯在一起的視頻數(shù)據(jù)格式。
            video/mpeg:MPEG格式的視頻,文件擴展名.mpg
        ⑥application:用于傳輸應(yīng)用程序數(shù)據(jù)或者二進制數(shù)據(jù); 默認(rèn)是application/octet-stream; 
            application/zip:PK-ZIP格式的壓縮文件,文件擴展名.zip
        ⑦message:用于包裝一個E-mail消息;
2)Content-transfer-encoding:這條語句指明了編碼轉(zhuǎn)換的方式。Content-transfer-encoding的值有5種----
    "7bit"、"8bit"、"binary"、"quoted-printable"和"base64"----其中"7bit"是缺省值,即不用轉(zhuǎn)化的ASCII字符。
3)Content-Length:返回數(shù)據(jù)流內(nèi)容長度
4)Accept-Ranges:返回數(shù)據(jù)流壓縮編碼
5)Server:服務(wù)器類型

四.Android相關(guān)基礎(chǔ)網(wǎng)絡(luò)編程

之所以說是Android基礎(chǔ)網(wǎng)絡(luò)編程,是因為我們講的東西都是比較原始的網(wǎng)絡(luò)連接方式。真正的開發(fā)中我們要考慮很多性能問題,比如多線程等。我們常用的網(wǎng)絡(luò)請求庫如OKHttp等也就是封裝的下面的內(nèi)容,只不過考慮了很多性能問題,做了優(yōu)化封裝。

1.Android網(wǎng)絡(luò)編程分為兩種:基于HTTP協(xié)議與基于Socket

在計算機網(wǎng)絡(luò)知識體系中,運輸層的TCP(傳輸控制協(xié)議)把連接作為最基本的抽象。TCP的連接有兩個端點,被稱為Socket,通過IP地址+端口號來區(qū)分來自不同應(yīng)用程序進程或網(wǎng)絡(luò)連接的通信,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。Client進程和Server進程之間是通過Socket讀寫數(shù)據(jù)進行通信的。
??JDK的java.net包下有兩個類:Socket和ServerSocket,在Client和Server建立連接成功后,兩端都會產(chǎn)生一個Socket實例,操作這個實例,完成所需的會話,而程序員就通過這些API進行網(wǎng)絡(luò)編程。 Socket連接過程分為三個步驟:服務(wù)器監(jiān)聽,客戶端請求,連接確認(rèn)Socket是對TCP/IP協(xié)議的封裝,他本身不是什么協(xié)議,而是一個調(diào)用接口(API)TCP/IP只是一個協(xié)議棧,必須要具體實現(xiàn),同時還要提供對外的操作接口,這就是Socket接口。通過Socket,我們才能使用TCP/IP協(xié)議,因此有了一系列我們知道的函數(shù)接口——connect、accept、send、read、write等。
??HTTP是應(yīng)用層的協(xié)議,主要解決如何包裝數(shù)據(jù),應(yīng)用層的協(xié)議有很多,除了HTTP協(xié)議以外還有FTP、TELNET等。Web使用HTTP協(xié)議作為應(yīng)用層協(xié)議,以封裝HTTP文本信息,然后使用TCP/IP做傳輸層協(xié)議將其發(fā)送到網(wǎng)絡(luò)上。我們在傳輸數(shù)據(jù)時可以只使用(傳輸層)TCP/IP協(xié)議,但那樣的話,如果沒有應(yīng)用層,便無法識別數(shù)據(jù)內(nèi)容。
??注意Socket對TCP/IP協(xié)議的封裝是跨傳輸層和應(yīng)用層的(不然在客戶端也無法調(diào)用)。
??由于通常情況下Socket鏈接即是TCP鏈接,因此Socket一旦建立,通信雙方即可開始相互發(fā)送數(shù)據(jù),直到雙方鏈接斷開,即不需要客戶端給服務(wù)器發(fā)送請求,服務(wù)器端也可以主動向客戶端發(fā)送數(shù)據(jù);而HTTP鏈接使用的是“請求—響應(yīng)”模式,不僅在請求時需要建立連接,而且客戶端向服務(wù)器發(fā)出請求后服務(wù)器端才會回復(fù)數(shù)據(jù),因此使用HTTP協(xié)議的客戶端需要一個“心跳包”去定時詢問服務(wù)器是否有新的數(shù)據(jù)并獲取。
??因此,Socket看起來是比較方便的。但是他的缺點也是很明顯的:客戶端到服務(wù)器之間往往是要穿過多個中間點的,比如路由器、網(wǎng)關(guān)、防火墻等,很多防火墻會默認(rèn)關(guān)閉長時間處于非活躍狀態(tài)的鏈接,所以長時間沒有數(shù)據(jù)更新的話很容易導(dǎo)致Socket鏈接中斷,因此需要輪詢告訴網(wǎng)絡(luò)這個該鏈接處于活躍狀態(tài),利用Socket的客戶端實現(xiàn)成本更高一些。
??一般來講,像QQ、微信這種即時通訊類型的客戶端就需要用Socket,而像一般論壇或者微博之類的用HTTP協(xié)議就可以了。

2.實現(xiàn)Android端的基于HTTP協(xié)議網(wǎng)絡(luò)傳輸

在Android中進行http傳輸可以使用HttpURLConnection或者HttpClient類連接URL,但是HttpClient谷歌已經(jīng)給他廢了,所以我們只講HttpURLConnection。
(1)“GET”方法:

GET讀取網(wǎng)頁流程.png

注:上述流程也是一般的網(wǎng)頁爬蟲爬取數(shù)據(jù)的流程。
第一步:指定URL資源并實現(xiàn)連接:

URL url = new URL(http://www.baidu.com);          //創(chuàng)建一個URL對象:
HttpURLConnection conn = (HttpURLConnection)url.openConnection(); //打開一個鏈接,用HttpURLConnection對象conn從網(wǎng)絡(luò)中獲取網(wǎng)頁數(shù)據(jù)。

面這句代碼這里實際上包含了兩步:
URLConnection urlConnection = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;
??URLConnection 與HttpURLConnection 使用的都是Java.net中的類,屬于Java標(biāo)準(zhǔn)接口,后者繼承自前者,差別在于后者只適用于http協(xié)議。從這里我們可以看出實際上 實現(xiàn)網(wǎng)絡(luò)連接的是URLConnection類,這里是把這個類的對象強制轉(zhuǎn)化為了HttpURLConnection類型,具體知識可以了解JAVA網(wǎng)絡(luò)編程方面的知識。
第二步:設(shè)置連接參數(shù)的方法:

conn.setConnectTimeout(6*1000);     //設(shè)置連接超時,如果網(wǎng)絡(luò)不好,Android系統(tǒng)會在設(shè)置的時間內(nèi)收回資源終端操作
conn.setDoOutput(false);           //如果是POST請求,參數(shù)在正文中所以要允許輸出流,如果是get請求不用輸出了因為參數(shù)是在URL之后。
conn.setDoInput(true);                //響應(yīng)數(shù)據(jù)的接收,所以我們要允許數(shù)據(jù)輸入流。
conn.setUesCaches(false); //設(shè)置不用緩存,否則文件過的大的時候可能會造成內(nèi)存溢出。
conn.setRequestMethod(“GET”);   //設(shè)置請求方式,注意不設(shè)的話默認(rèn)情況下也是GET,因此setDoOutput默認(rèn)情況下是false,setDoInput默認(rèn)是true。
conn.setRequestProperty(
              URLEncoder.encode("Content-type","UTF-8"),
              URLEncoder.encode("application/x-java-serialized-object","UTF-8")
          );    //setRequestProperty這個函數(shù)中的參數(shù)也就是上面的Content-Type參數(shù)。

第三步:建立和遠程資源之間的實際連接

conn.connect();              //建立一個TCP連接,這里直接封裝好了,不用三次握手。
            //注意第二步中的那些屬性都必須在connect()之前。

第四步:接收請求響應(yīng)數(shù)據(jù):
??對于GET請求,不用管outputStream,調(diào)用conn.getInputStream()方法來創(chuàng)建輸入流即可接收Http的請求響應(yīng)。如果是使用POST方法則需要調(diào)用conn.getOutputStream()方法來創(chuàng)建輸出流,通過輸出流將Http請求實體中的請求參數(shù)傳遞到服務(wù)器上。

InputStream inStream = conn.getInputStream();
InputStreamReader in = new InputStreamReader(inStream );    //無論是GET還是POST,HTTP的請求實際上直到調(diào)用
        //coon.getInputStream()才真正發(fā)送出去,對于GET而言不需要outputSream操作,因為已經(jīng)DoOutput(false)了。
BufferedReader buffer = new BufferedReader(in);
If(conn.getResponseCode()!= 200){                  //對響應(yīng)碼進行判斷
     Throw new RuntimeException(“請求URL失敗”);
}else{
     //輸入流讀取字節(jié),再將它們轉(zhuǎn)化成字符,讀取內(nèi)容:
     String inputLine = null;
     String resultData = null;
     While((inputLine = buffer.readeLine())!=null){
           resultData += inputLine;        //利用循環(huán)來讀取數(shù)據(jù)
     }
}
conn.disconnect();                        //及時關(guān)閉
(2)"POST”方法

POST用法與GET方法基本一致,不同的是POST是要往服務(wù)器端發(fā)送數(shù)據(jù)(用于數(shù)據(jù)更新等),它的參數(shù)是放在HTTP正文內(nèi)的,所以這個時候就要用到conn.getOutputStream();方法。
??還有就是GET請求可以緩存(當(dāng)然也可以手動不緩存),Post請求不能緩存——至于為什么不能緩存,這個過于博大精深,有待于進一步研究。
第一步:指定URL資源并實現(xiàn)連接

URL url = new URL(http://localhost:8080/TestHttpURLConnectionPro.do);     
HttpURLConnection conn = (HttpURLConnection)url.openConnection();

第二步:指定鏈接參數(shù)的方法

conn.setDoOutput(true);      //設(shè)置是否向HttpURLConnection輸出,這個是post請求,參數(shù)要放在http協(xié)議正文中,所以必須設(shè)為true,默認(rèn)狀態(tài)下為false.
conn.setDoInput(true);      //設(shè)置是否從HttpURLConnection讀入信息。
conn.setUesCaches(false);   //POST不能用緩存
conn.setRequestProperty(“Content-type”,”application/x-iava-serialized-object”); //上面這一步也要看我們與服務(wù)器之間的約定
conn.setRequestMethod(“POST”); 

第三步:建立和遠程資源之間的實際連接
??OutputStream不是一個網(wǎng)絡(luò)流,充其量只是一個字符串流,往其中寫的東西不會立即發(fā)送到網(wǎng)絡(luò)上,而是存在于內(nèi)存緩存區(qū)等到outputStream流關(guān)閉時,根據(jù)其內(nèi)容生成HTTP正文
??至此,http請求的東西已經(jīng)全部準(zhǔn)備好了,在第四步調(diào)用getInputStream()函數(shù)調(diào)用的時候,就會把準(zhǔn)備好的http請求發(fā)送到服務(wù)器,并且返回一個輸入流,用于讀取服務(wù)器對于此次請求的返回信息
??由于http請求在getInputStream()函數(shù)調(diào)用的時候已經(jīng)發(fā)送出去了(包括http頭和正文body),因此在這之后對conn對象進行設(shè)置(對http頭的信息修改)或者寫入outputStream(對正文進行修改)都是沒有意義的,執(zhí)行這些操作會發(fā)生異常。

conn.connect();
OutputStream outStream = conn.getOutputStream();    //此處OutputStream會隱含進行connect,所以上面的connect()方法可以不用寫。
DataOutputStream out= new DataOutputStream (outStream );

第四步:寫入并發(fā)送我們的實際數(shù)據(jù)

String content = “firstname”+URLEncoder.encode(“LIU”,”UTF-8”);
out.writeBytes(content);   //向?qū)ο罅鲗懭霐?shù)據(jù),這些數(shù)據(jù)將存到內(nèi)存緩沖區(qū)
out.flush();       //刷新對象輸出流,將任何字節(jié)都寫入潛在流中。
out.close();      //關(guān)閉流對象,此時不能再向?qū)ο罅鲗懭肴魏螖?shù)據(jù),先前寫入的數(shù)據(jù)存在于內(nèi)存緩沖區(qū)中,在調(diào)用下邊的getInputStream()函數(shù)時才把準(zhǔn)備好的請求發(fā)送到服務(wù)器。

InputStream inStream = conn.getInputStream();    //注意啦,這里和GET請求中的一樣
InputStreamReader in = new InputStreamReader(inStream) ;//getInputStream()這個函數(shù)的作用有兩個,一個是發(fā)送
                //請求(包括上面我們寫入的數(shù)據(jù)),另一個是讀取響應(yīng),即便是POST,你給服務(wù)器發(fā)送數(shù)據(jù)之后他還是要給你
                //傳一些數(shù)據(jù)回來的,不如說更新成功之類的。
BufferedReader buffer = new BufferReader(in);
if(conn.getResponseCode()!= 200){
     Throw new RuntimeException(“請求URL失敗”);
}else{
     String inputLine = null;
     String resultData = null;
     While((inputLine = buffer.readeLine())!=null){
           resultData += inputLine;
     }
}
conn.disconnect();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,434評論 6 152
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,914評論 18 139
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng),內(nèi)容來源于博客園作者MIN飛翔的HTTP協(xié)...
    Sivin閱讀 5,252評論 3 82
  • PS:簡書的網(wǎng)址真不是給人看的。。。我單獨開了一個網(wǎng)址可以重定向到我的簡書主頁。博客地址:flutterall.c...
    徐愛卿閱讀 6,976評論 21 97
  • 本文整理自MIN飛翔博客 [1] 1. 概念 協(xié)議是指計算機通信網(wǎng)絡(luò)中兩臺計算機之間進行通信所必須共同遵守的規(guī)定或...
    HoyaWhite閱讀 2,705評論 2 20