編輯:關於Android編程
在上一篇中我們討論了計算機網絡的體系結構和各層次的作用,在我們編程中TCP或UDP都提供了socket接口進行實現,實現的例子在上一篇中,這一篇我們主要討論一下Http協議,以及如何實現Http協議。
討論的問題:
Http協議的定義和內容。 Http協議的實現。HTTP的英文是(HyperText Transfer Protocol),即超文本傳輸協議,那麼什麼是超文本呢,超文本就是使用超文本標記語言HTML的文本。
首先互聯網上每一個資源都用一個URL所標記,URL的格式為:
http://<主機>:<端口>/<路徑>
它是應用層協議,規定了數據交互的格式內容。在運輸層,它采用了TCP協議保證了可靠運輸。而且HTTP協議是無狀態的,不過在HTTP/1.1中,添加了持續連接的功能。
下面就來看看HTTP報文的格式:
首先它有兩種報文:
- 請求報文(客戶端發送給服務器的報文)
- 響應報文(服務器返回給客戶端的報文)
而且HTTP是面向文本的,所以在報文中的每一個字段都是一些ASCII碼。
請求報文:
請求報文包括 請求行、請求頭部、請求數據
這裡面第一行是請求行,舉一個例子:
GET http://s2-im-notify.csdn.net/socket.io/1/xhr-polling/4-edxSXeVplQjhPcXY2V?t=1476099463098 HTTP/1.1
Host: s2-im-notify.csdn.net
Connection: keep-alive
Origin: http://blog.csdn.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Accept: */*
Referer: http://blog.csdn.net/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
請求行:
在這個請求中我們看到這個請求行為第一行,下面的都是請求頭了,這個並沒有請求數據。這個請求中請求方法為GET,GET是我們常用的請求方法之一,除了GET,我們還常用POST進行請求。
下面列舉出請求方法,不過最常用的還是GET和POST:
OPTION:請求一些選項的信息 GET:請求讀取由URL所標志的信息 HEAD:請求讀取由URL所標志的信息的首部 POST:給服務器發送信息 PUT:在指明的URL下存儲一個文檔 DELETE:刪除指明的URL所標志的資源 TRACE:用來進行環回測試的請求報文
CONNECT:用於代理服務器
請求報頭:
請求報頭有許多種,下面說一些常見的請求頭:
Host域:指定請求的服務器地址,在HTTP/1.1中請求必須包含主機頭域,否則系統會以400狀態碼返回。
Connection域:指定與連接相關的屬性,比如上面例子中Connection: keep-alive,代表保持連接。 User-Agent域:發送請求的應用程序名稱。 Accept-Charset域:通知服務端可以發送的編碼格式。 Accept-Encoding域:通知服務端可以發送的數據壓縮格式。 Accept-Language域:通知服務器可以發送的語言。 Accept域: 告訴WEB服務器自己接受什麼介質類型,/ 表示任何類型,type/* 表示該類型下的所有子類型。 Referer域:發送請求頁面URL。浏覽器向 WEB 服務器表明自己是從哪個 網頁/URL 獲得/點擊 當前請求中的網址/URL。 Pramga域:主要使用 Pramga: no-cache,相當於 Cache-Control: no-cache。 Date域:表示消息發出的時間。 Cookie域:設置Cookie相關的。 Cache-Control域:使用的緩存機制(在請求時和響應時它的值不同,響應的下文會說到)。
no-cache(不要緩存的實體,要求現在從服務器去取)
max-age:(只接受 Age 值小於 max-age 值,並且沒有過期的對象)
max-stale:(可以接受過去的對象,但是過期時間必須小於 max-stale 值)
min-fresh:(接受其新鮮生命期大於其當前 Age 跟 min-fresh 值之和的緩存對象)。
響應報文
類比請求報文,響應報文同樣也包括 響應行、響應頭、響應數據
下面也是用一個例子來看一下:
HTTP/1.1 200 OK
Server: openresty
Date: Mon, 10 Oct 2016 12:25:25 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private
Access-Control-Allow-Origin: *
Content-Encoding: gzip
6b0
X[ F~G ? $ I @ eiWb
j }Bc{ k ]{r F+A/Z} T U/- [ cJ _ _
6 g s wΜs ? #g / sa
O [^
*** FIDDLER: RawDisplay truncated at 128 characters. Right-click to disable truncation. ***
響應行:
第一行為響應行,對於響應行,我們可以看出狀態碼(也成為響應碼)為200。
關於狀態碼,都是由三位數字組成的,分為五大類:
1XX :表示通知信息,如請求收到了或者正在進行處理。 2XX:表示成功,其中最常見的便是200,表示請求成功。 3XX:表示重定向,如要完成請求還必須采取進一步的行動。 4XX:表示客戶端出錯,最常見的就是404,表示未找到。 5XX:表示服務器出錯,比如服務器失效無法完成請求。
除了上面的還有一些常見的響應頭,比如:
HTTP/1.1 202 Accepted //表示接受
HTTP/1.1 400 Bad Request //表示錯誤的請求
HTTP/1.1 404 Not Found //表示未找到
HTTP/1.1 301 Moved Permanently //永久性的轉移了
響應頭:
列舉一下常用的頭部,(PS:在百度時發現一篇寫關於這個頭部和狀態碼非常詳細的文章,我有的頭部也參考的他的博客,分享給大家:/net/201607/528141.html) Age域:當代理服務器用自己緩存的實體去響應請求時,用該頭部表明該實體從產生到現在經過多長時間了。 Accept-Ranges域:WEB服務器表明自己是否接受獲取其某個實體的一部分(比如文件的一部分)的請求。bytes:表示接受,none:表示不接受。 Content-Type域:服務器通知客戶端它響應的對象類型,如Content-Type:application/json。
Content-Range域:服務器表明該響應包含的部分對象為整個對象的哪個部分。
Content-Length:服務器通知響應包含對象的長度。
Content-Language:服務器通知客戶端響應的語言。 Content-Encoding:服務器通知客戶端響應數據的壓縮格式,在上面的例子中可以看出壓縮格式為gzip。 Connection:代表是否需要持久連接。 Expired:WEB服務器表明該實體將在什麼時候過期,對於過期了的對象,只有在跟WEB服務器驗證了其有效性後,才能用來響應客戶請求。 Last-Modified: 服務器認為對象的最後修改時間,比如文件的最後修改時間,動態頁面的最後產生時間等等。 Location:服務器告訴浏覽器,試圖訪問的對象已經被移到別的位置了,到該頭部指定的位置去取。 Proxy-Authenticate:代理服務器響應浏覽器,要求其提供代理身份驗證信息。 Server: 服務器表明自己是什麼軟件及版本等信息。 Refresh:表示浏覽器應該在多少時間之後刷新文檔,以秒計。
響應數據:
一般來說響應數據也會有一定的格式,上面的例子中因為數據用gzip壓縮了,所以顯示的為亂碼。現在用的最火的為json格式數據,下面的例子為一個請求的響應數據,為json格式:
{“status”:true,”error”:”“,”data”:{“id”:010101,”url”:”http://blog.csdn.net/liushuaiq/article/details/52779689“}}
二、Http協議的實現
上一篇中我們使用了java提供的socket進行了數據的傳輸,socket是對tcp或udp的封裝,對於應用層沒有實現,這一篇就對上一篇進行進一步的拓展,實現Http協議。
我們只需要在socket傳輸數據的基礎上,進行進一步的格式化數據就可以了。
首先我封裝了一個請求實體。
package com.liushuai.model;
import java.util.List;
public class Request {
private RequestLine requestLine;
private List requestHeaders;
private RequestBody requestBody;
public Request() {
super();
}
public Request(RequestLine requestLine, List requestHeaders, RequestBody requestBody) {
super();
this.requestLine = requestLine;
this.requestHeaders = requestHeaders;
this.requestBody = requestBody;
}
public RequestLine getRequestLine() {
return requestLine;
}
public void setRequestLine(RequestLine requestLine) {
this.requestLine = requestLine;
}
public List getRequestHeaders() {
return requestHeaders;
}
public void setRequestHeaders(List requestHeaders) {
this.requestHeaders = requestHeaders;
}
public RequestBody getRequestBody() {
return requestBody;
}
public void setRequestBody(RequestBody requestBody) {
this.requestBody = requestBody;
}
}
其中裡面還進行進一步封裝了請求行和請求頭和請求體,代碼如下:
package com.liushuai.model;
/**
* 請求行實體
*
* @author LiuShuai
*
*/
public class RequestLine {
/**
* 請求方法
*/
private String method;
/**
* 請求的 URL
*/
private String url;
/**
* 版本
*/
private String version;
public RequestLine() {
super();
}
public RequestLine(String method, String url, String version) {
super();
this.method = method;
this.url = url;
this.version = version;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
package com.liushuai.model;
/**
* 請求頭部實體
*
* @author LiuShuai
*
*/
public class RequestHeader {
/**
* 頭部名稱
*/
private String name;
/**
* 頭部域值
*/
private String value;
public RequestHeader() {
super();
}
public RequestHeader(String name, String value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
package com.liushuai.model;
/**
* 請求體
*
* @author LiuShuai
*
*/
public class RequestBody {
private String requestBody;
public RequestBody() {
super();
}
public RequestBody(String requestBody) {
super();
this.requestBody = requestBody;
}
public String getRequestBody() {
return requestBody;
}
public void setRequestBody(String requestBody) {
this.requestBody = requestBody;
}
}
下面是在socket的基礎上將數據進行封裝,然後格式化為Http協議中要求的格式輸出:
package com.liushuai.client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import javax.management.relation.Relation;
import javax.sql.rowset.serial.SerialArray;
import com.liushuai.model.Request;
import com.liushuai.model.RequestHeader;
import com.liushuai.model.RequestLine;
import com.liushuai.server.MyServer;
public class MyClient {
public static void main(String[] args) {
InetAddress inet4Address;
try {
inet4Address = Inet4Address.getLocalHost();
Socket socket = new Socket("123.126.51.32", 80);
//請求行
RequestLine rLine = new RequestLine("GET",
"http://download.pinyin.sogou.com/picface/interface/cellupdate.php?h=53B24101A353034C6F75D2DB4436AA80&v=8.0.0.8381&r=0000_sogou_pinyin_win10a&ver=1.0.0.1464&cellid=80|77|79|78|2|37|41|47|52|54&cellver=2|3|2|2|4|2|4|5|3|6",
"HTTP/1.1");
//請求頭
RequestHeader rh1 = new RequestHeader("User-Agent", "SogouComponentAgent");
RequestHeader rh2 = new RequestHeader("Host", "download.pinyin.sogou.com");
RequestHeader rh3 = new RequestHeader("Pragma", "no-cache");
RequestHeader rh4 = new RequestHeader("Cookie",
"YYID=53B24101A353034C6F75D2DB4436AA80; IMEVER=8.0.0.8381; IPLOC=CN3702");
List rhs = new ArrayList<>();
rhs.add(rh1);
rhs.add(rh2);
rhs.add(rh3);
rhs.add(rh4);
//構造請求,這裡面讓請求體為空了
Request request = new Request(rLine, rhs, null);
// 拼裝一個Http請求
StringBuffer requestString = new StringBuffer();
// 拼裝請求行
RequestLine reqLine = request.getRequestLine();
StringBuffer line = new StringBuffer();
line.append(reqLine.getMethod()).append(" ").append(reqLine.getUrl()).append(" ")
.append(reqLine.getVersion()).append("\r\n");
requestString.append(line);
// 拼裝請求頭部
List requestHeaders = request.getRequestHeaders();
StringBuffer headers = new StringBuffer();
for (RequestHeader h : requestHeaders) {
headers.append(h.getName()).append(":").append(h.getValue()).append("\r\n");
}
requestString.append(headers).append("\r\n");
if (request.getRequestBody()!= null) {
requestString.append(request.getRequestBody().getRequestBody());
}
//向服務器發送請求的數據,數據已經拼裝成了Http請求報文的格式
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
printWriter.print(requestString.toString());
printWriter.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuffer responseString = new StringBuffer();
String servervInfo ;
//讀取服務器的響應,不過這地方會阻塞,因為Java I/O是線程阻塞的,優化一下代碼應當使用Java NIO
while((servervInfo = reader.readLine())!="\r\n"&&servervInfo!=null){
System.out.println(servervInfo);
responseString.append(servervInfo+"\n");
}
System.out.println("服務器端發送的數據為--->" + responseString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
這就簡單的模仿了一下Http的一次請求,我將返回結果進行了打印:
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 11 Oct 2016 12:28:29 GMT
Content-Type: application/octet-stream
Content-Length: 0
Connection: keep-alive
X-Powered-By: PHP/7.0.8
Pragma: cache
Cache-Control: public, must-revalidate, max-age=0
Accept-Ranges: bytes
Content-Disposition: filename="SGPicFaceCellList.ini"
可以看出這次是請求成功了,返回的格式也符合Http中要求的格式。
這一篇就對HTTP協議進行了一次詳細的介紹,下一篇中我准備分析一下當前最火的Android網絡框架OkHttp源碼中的實現。
今天要做一個帶箭頭的圓角矩形菜單,大概長下面這個樣子: 要求頂上的箭頭要對准菜單錨點,菜單項按壓反色,菜單背景色和按壓色可配置。最簡單的做法就是讓UX給個三角形
android 4.0以後EditText的風格默認變成了只有下面有一條橫線。如圖: 如果想要變成原來的風格,只需要在EditText屬性中加入an
什麼是banner組件?在許多Android應用上,比如愛奇藝客戶端、百度美拍、應用寶等上面,都有一個可以手動滑動的小廣告條,這就是banner,實際應用中的banner
ES文件浏覽器怎麼開啟保護跟打開網絡保護。ES文件浏覽器很強大的一個軟件。有時我們防止別人進入手機文件,偷看我們的文件的時候,我們設置對ES文件浏覽器開啟保