HTTP(HyperText Transfer Protocol),超文本傳輸協議,是一個基於TCP實現的應用層協議。css
HTTP1.0的報文有兩種類型:請求和相應。其報文格式分別爲:html
請求方法 URL HTTP/版本號
請求首部字段(可選)
空行
body(只對Post請求有效)
複製代碼
例如:linux
GET http://m.baidu.com/ HTTP/1.1
Host m.baidu.com
Connection Keep-Alive
...// 其餘header
key=Android
複製代碼
HTTP/版本號 返回碼 返回碼描述
應答首部字段(可選)
空行
body
複製代碼
例如:算法
HTTP/1.1 200 OK
Content-Type text/html;charset=UTF-8
...// 其餘header
<html>...
複製代碼
使用HTTP協議訪問資源是經過URL(Uniform Resource Locator)統一資源定位符來實現的。URL的格式以下:編程
scheme://host:port/path?query
scheme: 表示協議,如Http, Https, Ftp等;
host: 表示所訪問資源所在的主機名:如:www.baidu.com;
port: 表示端口號,默認爲80;
path: 表示所訪問的資源在目標主機上的儲存路徑;
query: 表示查詢條件;
例如: http://www.baidu.com/search?words=Baidu
複製代碼
HTTP首部字段由字段名和字段值組成,中間以":"分隔,如Content-Type: text/html.其中,同一個字段名可對應多個字段值。瀏覽器
HTTP的報文字段分爲5種:緩存
Http請求中支持的報文字段。安全
Accept:客戶端可以處理的媒體類型。如text/html, 表示客戶端讓服務器返回html類型的數據,若是沒有,返回text
類型的也能夠。媒體類型的格式通常爲:type/subType, 表示優先請求subType類型的數據,若是沒有,返回type類型
數據也能夠。
常見的媒體類型:
文本文件:text/html, text/plain, text/css, application/xml
圖片文件:iamge/jpeg, image/gif, image/png;
視頻文件:video/mpeg
應用程序使用的二進制文件:application/octet-stream, application/zip
Accept字段可設置多個字段值,這樣服務器依次進行匹配,並返回最早匹配到的媒體類型,固然,也可經過q參數來設置
媒體類型的權重,權重越高,優先級越高。q的取值爲[0, 1], 可取小數點後3位,默認爲1.0。例如:
Accept: text/html, application/xml; q=0.9, */*
Accept-Charset: 表示客戶端支持的字符集。例如:Accept-Charset: GB2312, ISO-8859-1
Accept-Encoding: 表示客戶端支持的內容編碼格式。如:Accept-Encoding:gzip
經常使用的內容編碼:
gzip: 由文件壓縮程序gzip生成的編碼格式;
compress: 由Unix文件壓縮程序compress生成的編碼格式;
deflate: 組合使用zlib和deflate壓縮算法生成的編碼格式;
identity:默認的編碼格式,不執行壓縮。
Accept-Language:表示客戶端支持的語言。如:Accept-Language: zh-cn, en
Authorization:表示客戶端的認證信息。客戶端在訪問須要認證的也是時,服務器會返回401,隨後客戶端將認證信息
加在Authorization字段中發送到服務器後,若是認證成功,則返回200. 如Linux公社下的Ftp服務器就是這種流程:
ftp://ftp1.linuxidc.com。
Host: 表示訪問資源所在的主機名,即URL中的域名部分。如:m.baidu.com
If-Match: If-Match的值與所請求資源的ETag值(實體標記,與資源相關聯。資源變化,實體標記跟着變化)一致時,
服務器才處理此請求。
If-Modified-Since: 用於確認客戶端擁有的本地資源的時效性。 若是客戶端請求的資源在If-Modified-Since指定
的時間後發生了改變,則服務器處理該請求。如:If-Modified-Since:Thu 09 Jul 2018 00:00:00, 表示若是客戶
端請求的資源在2018年1月9號0點以後發生了變化,則服務器處理改請求。經過該字段咱們可解決如下問題:有一個包含大
量數據的接口,且實時性較高,咱們在刷新時就可以使用改字段,從而避免多餘的流量消耗。
If-None-Match: If-Match的值與所請求資源的ETag值不一致時服務器才處理此請求。
If-Range: If-Range的值(ETag值或時間)與所訪問資源的ETag值或時間相一致時,服務器處理此請求,並返回
Range字段中設置的指定範圍的數據。若是不一致,則返回全部內容。If-Range其實算是If-Match的升級版,由於它
的值不匹配時,依然可以返回數據,而If-Match不匹配時,請求不會被處理,須要數據時需再次進行請求。
If-Unmodified-Since:與If-Modified-Since相反,表示請求的資源在指定的時間以後未發生變化時,才處理請求,
不然返回412。
Max-Forwards:表示請求可通過的服務器的最大數目,請求每被轉發一次,Max-Forwards減1,當Max-Forwards爲0
時,所在的服務器將再也不轉發,而是直接作出應答。經過此字段可定位通訊問題,好比以前支付寶光纖被挖斷,就可經過設
置Max-Forwards來定位大概的位置。
Proxy-Authorization:當客戶端接收到來自代理服務器的認證質詢時,客戶端會將認證信息添加到
Proxy-Authorization來完成認證。與Authorization相似,只不過Authorization是發生在客戶端與服務端之間。
Range:獲取部分資源,例如:Range: bytes=500-1000表示獲取指定資源的第500到1000字節之間的內容,若是服務器
可以正確處理,則返回206做爲應答,表示返回了部分數據,若是不能處理這種範圍請求,則以200做爲應答,返回完整的
數據,
Referer:告知服務器請求是從哪一個頁面發起的。例如在百度首頁中搜索某個關鍵字,結果頁面的請求頭部就會有這個字段,
其值爲https://www.baidu.com/。經過這個字段可統計廣告的點擊狀況。
User-Agent:將發起請求的瀏覽器和代理名稱等信息發送給服務端,例如:
User-Agent: Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/63.0.3239.84 Mobile Safari/537.36
複製代碼
Http應答中支持的報文字段。服務器
Accept-Ranges: 服務端告知客戶端本身可以處理範圍請求,其值有兩種:bytes,none.其中bytes表示可處理,none
表示不能處理。
Age:服務端告知客戶端,源服務器(而不是緩存服務器)在多久以前建立了響應。
單位爲秒。
ETag: 實體資源的標識,可用來請求指定的資源。
Location:請求的資源所在的新位置。
Proxy-Authenticate:將代理服務器須要的認證信息發送給客戶端。
Retry-After:服務端告知客戶端多久以後再重試,通常與503和3xx重定向類型的應答一塊兒使用。
Server:告知服務端當前使用的HTTP服務器應用程序的相關信息。
WWW-Authenticate:告知客戶端適用於所訪問資源的認證方案,如Basic或Digest。401的響應中確定帶有
WWW-Authenticate字段。
複製代碼
Allow:通知客戶端,服務器所支持的請求方法。但服務器收到不支持的請求方法時,會以405(Method Not Allowed)
做爲響應。
Content-Encoding:告知客戶端,服務器對資源的內容編碼。
Content-Language:告知客戶端,資源所使用的天然語言。
Content-Length:告知客戶端資源的長度
Content-Location:告知客戶端資源所在的位置。
Content-Type:告知客戶端資源的媒體類型,取值同請求首部字段中的Accept。
Expires:告知客戶端資源的失效日期。可用於對緩存的處理。
Last-Modified:告知客戶端資源最後一次修改的時間。
複製代碼
便可在HTTP請求中使用,也可在HTTP應答中使用的報文字段。網絡
Cache-Control:控制緩存行爲;
Connection:管理持久鏈接,設置其值爲Keep-Alive可實現長鏈接。
Date:建立HTTP報文的日期和時間。
Pragma:Http/1.1以前的歷史遺留字段,僅做爲HTTP/1.0向後兼容而定義,雖然是通用字段,當一般被使用在客戶單的
請求中,如Pragma: no-cache, 表示客戶端在請求過程當中不循序服務端返回緩存的數據;
Transfer-Encoding:規定了傳輸報文主題時使用的傳輸編碼,如Transfer-Encoding: chunked
Upgrade: 用於檢查HTTP協議或其餘協議是否有可以使用的更高版本。
Via:追蹤客戶端和服務端之間的報文的傳輸路徑,還可避免會環的發生,因此在通過代理時必須添加此字段。
Warning:Http/1.1的報文字段,從Http/1.0的AfterRetry演變而來,用來告知用戶一些與緩存相關的警告信息。
複製代碼
這些字段不是HTTP協議中定義的,但被普遍應用於HTTP請求中。
Cookie:屬於請求型報文字段,在請求時添加Cookie, 以實現HTTP的狀態記錄。
Set-Cookie:屬於應答型報文字段。服務器給客戶端傳遞Cookie信息時,就是經過此字段實現的。
Set-Cookie的字段屬性:
NAME=VALUE:賦予Cookie的名稱和值;
expires=DATE: Cookie的有效期;
path=PATH: 將服務器上的目錄做爲Cookie的適用對象,若不指定,則默認爲文檔所在的文件目錄;
domin=域名:做爲Cookies適用對象的域名,若不指定,則默認爲建立Cookie的服務器域名;
Secure: 僅在HTTPS安全通訊是纔會發送Cookie;
HttpOnly: 使Cookie不能被JS腳本訪問;
如:Set-Cookie:BDSVRBFE=Go; max-age=10; domain=m.baidu.com; path=/
複製代碼
狀態碼 | 類別 | 描述 |
---|---|---|
1xx | Informational(信息性狀態碼) | 請求正在被處理 |
2xx | Success(成功狀態碼) | 請求處理成功 |
3xx | Redirection(重定向狀態碼) | 須要進行重定向 |
4xx | Client Error(客戶端狀態碼) | 服務器沒法處理請求 |
5xx | Server Error(服務端狀態碼) | 服務器處理請求時出錯 |
瞭解應答狀態碼的含義,有助於咱們在開發過程當中定位問題,好比出現4xx, 咱們首先須要檢查的是請求是否有問題,而出現5xx時,則應讓服務端作相應的檢查工做。
HTTP是基於TCP協議的一種應用層協議,那咱們就可經過系統提供的Socket來實現,實現步驟以下:
這裏的代碼依然使用上一篇的理論知識中引用的代碼:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
if(argc != 2)
{
printf("\n Usage: %s <ip of server> \n",argv[0]);
return 1;
}
memset(recvBuff, '0',sizeof(recvBuff));
// 建立socket
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
// 設置IP和端口
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\n inet_pton error occured\n");
return 1;
}
// 鏈接到指定的IP和端口 -> 鏈接成功後即三次握手完成
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
// 構造HTTP頭部
char *str = "HEAD http://www.baidu.com/ HTTP/1.1\r\n"
"Host: www.baidu.com\r\n"
"\r\n";
// 將HTTP請求發送到服務端
int len = write(sockfd, str, strlen(str) + 1);
if (len > 0) {
printf("request send successful!\n\n");
}
// 讀取服務端返回的數據
while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
{
recvBuff[n] = 0;
if(fputs(recvBuff, stdout) == EOF)
{
printf("\n Error : Fputs error\n");
}
}
if(n < 0)
{
printf("\n Read error \n");
}
close(sockfd);
return 0;
}
複製代碼
經過gcc編譯以後運行可得如下結果:
$ ./client 58.217.200.13
request send successful!
HTTP/1.1 200 OK
Date: Mon, 15 Jan 2018 12:21:47 GMT
Server: Apache
P3P: CP=" OTI DSP COR IVA OUR IND COM "
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Set-Cookie: BAIDUID=37229A83CAD417143F243CF4BF632CD4:FG=1; expires=Tue, 15-Jan-19 12:21:47 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1
Set-Cookie: BAIDUID=37229A83CAD41714BC95B90FC2266CF0:FG=1; expires=Tue, 15-Jan-19 12:21:47 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1
Last-Modified: Wed, 08 Feb 2017 07:55:35 GMT
ETag: "1cd6-5480030886bc0"
Accept-Ranges: bytes
Content-Length: 7382
Cache-Control: max-age=1
Expires: Mon, 15 Jan 2018 12:21:48 GMT
Vary: Accept-Encoding,User-Agent
Connection: Keep-Alive
Content-Type: text/html
複製代碼
從結果能夠看出,咱們已實現了HTTP的請求過程。其應答過程的實現也是一樣的道理。根據上一篇文章網絡編程之理論篇中介紹的Socket服務端的編程步驟,結合HTTP應答報文的格式,便可快速實現HTTP的應答過程。 固然,要實現完整的HTTP協議仍是有不少細節須要處理的。這裏只是演示了一下HTTP的實現過程,便於對HTTP協議的實現有一個簡單的瞭解。下一篇文章經過JDK提供的HttpUrlConnection來深刻理解HTTP的實現過程。
《圖解HTTP》