超簡潔的實(shí)例 ——關(guān)于HTTP協(xié)議分析

網(wǎng)絡(luò)通信的本質(zhì)是兩臺(tái)計(jì)算機(jī)上的兩個(gè)進(jìn)程之間的通信。比如蒿涎,瀏覽器進(jìn)程和新浪服務(wù)器上的某個(gè)Web服務(wù)進(jìn)程在通信哀托,而QQ進(jìn)程是和騰訊的某個(gè)服務(wù)器上的某個(gè)進(jìn)程在通信。

當(dāng)我們?cè)L問新浪的時(shí)候劳秋,發(fā)生了什么仓手?
本地電腦上的一個(gè)進(jìn)程(瀏覽器)向 新浪的服務(wù)器發(fā)起一個(gè)tcp的連接請(qǐng)求。這個(gè)請(qǐng)求的格式是什么玻淑?

下面寫一個(gè)python實(shí)現(xiàn)的例子嗽冒,建立一個(gè)socket,然后連接新浪补履,連接之后添坊,發(fā)送一個(gè)字符串。代碼如下:

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('www.sina.com.cn',80))
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

當(dāng)建立連接之后箫锤,本地進(jìn)程向新浪的服務(wù)器發(fā)送的消息的格式是上面這段代碼贬蛙。

GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n

這個(gè)字符串,其實(shí)就是http協(xié)議的 request請(qǐng)求谚攒。


下面討論的是http協(xié)議的格式:

http協(xié)議分成兩個(gè)大的部分阳准,一個(gè)是請(qǐng)求,一個(gè)是相應(yīng)馏臭。無論是請(qǐng)求還是相應(yīng)都包含兩個(gè)部分野蝇,一個(gè)是header,另外一個(gè)是body位喂。(body是可選 的)

HTTP GET請(qǐng)求的格式:

GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

注意:每個(gè)Header一行一個(gè)浪耘,換行符是\r\n。

HTTP POST請(qǐng)求的格式:

POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

注意:當(dāng)遇到連續(xù)兩個(gè)\r\n時(shí)塑崖,Header部分結(jié)束七冲,后面的數(shù)據(jù)全部是Body。

HTTP響應(yīng)的格式:

200 OK
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

再次注意:HTTP響應(yīng)如果包含body规婆,也是通過\r\n\r\n來分隔的澜躺。

請(qǐng)?jiān)俅巫⒁猓珺ody的數(shù)據(jù)類型由Content-Type頭來確定抒蚜,如果是網(wǎng)頁掘鄙,Body就是文本,如果是圖片嗡髓,Body就是圖片的二進(jìn)制數(shù)據(jù)操漠。


通過上面的描述,利用socket寫一個(gè)小的demo,理解一下http協(xié)議

思路:在本地創(chuàng)建一個(gè)socket浊伙,向新浪的服務(wù)器發(fā)起連接撞秋,然后偽造一個(gè)request請(qǐng)求。請(qǐng)求如下:

GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n
image.png

執(zhí)行如下代碼:

#coding:utf-8
import socket

#創(chuàng)建tcp socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立鏈接
s.connect(('www.sina.com.cn',80))
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

#創(chuàng)建一個(gè)buff等待接受
buffer=[]
while True:
    d=s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break;
#把接受緩存的數(shù)據(jù)都保存到data
data = b''.join(buffer)
print (data)

#斷開socket
s.close()

#把網(wǎng)頁的header和body分離
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的數(shù)據(jù)寫入文件:
with open('sina.html', 'wb') as f:
    f.write(html)

運(yùn)行結(jié)果:


image.png
image.png
image.png

PS:
HTTP之狀態(tài)碼
狀態(tài)代碼有三位數(shù)字組成嚣鄙,第一個(gè)數(shù)字定義了響應(yīng)的類別吻贿,共分五種類別:
1xx:指示信息--表示請(qǐng)求已接收,繼續(xù)處理
2xx:成功--表示請(qǐng)求已被成功接收哑子、理解舅列、接受
3xx:重定向--要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作
4xx:客戶端錯(cuò)誤--請(qǐng)求有語法錯(cuò)誤或請(qǐng)求無法實(shí)現(xiàn)
5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求

PPS:
常見狀態(tài)碼:
200 OK //客戶端請(qǐng)求成功
400 Bad Request //客戶端請(qǐng)求有語法錯(cuò)誤,不能被服務(wù)器所理解
401 Unauthorized //請(qǐng)求未經(jīng)授權(quán)帐要,這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用
403 Forbidden //服務(wù)器收到請(qǐng)求弥奸,但是拒絕提供服務(wù)
404 Not Found //請(qǐng)求資源不存在,eg:輸入了錯(cuò)誤的URL
500 Internal Server Error //服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤
503 Server Unavailable //服務(wù)器當(dāng)前不能處理客戶端的請(qǐng)求其爵,一段時(shí)間后可能恢復(fù)正常

PPPS: 補(bǔ)充一個(gè)小例子冒冬,提供一個(gè)掉坑的例子
打算寫一個(gè)模擬并發(fā)請(qǐng)求的壓力測試demo,核心的思路就是多進(jìn)程+每個(gè)進(jìn)程發(fā)送http請(qǐng)求摩渺。要做到不錯(cuò)的性能,打算用c去寫横侦。

問題是這樣的,在構(gòu)建http請(qǐng)求的時(shí)候绰姻,

    char buf[1500];
    strcpy(request,"GET / HTTP/1.0");
    strcat(request,"\r\n");
    strcat(request,"User-Agent: WebBench 1.5");
    strcat(request,"\r\n");
    strcat(request,"Host: localhost");
    strcat(request,"\r\n");
    //bug 出現(xiàn)在這里枉侧,剛開始沒有加上這一行。http get請(qǐng)求每一行是通過\r\n 來換行的
    //結(jié)尾的標(biāo)識(shí)是通過兩個(gè)\r\r 來表示狂芋,但是第一次的時(shí)候,我只寫了一個(gè)翼虫。
    //但是把請(qǐng)求打印出來屡萤,是看不來少了一個(gè)\r\n的,一通好找死陆,找不到bug
    //最后,我測試用的服務(wù)器是nginx,去看nginx的log
    //看到一個(gè)log里面的狀態(tài)碼是400劈愚,400對(duì)應(yīng)的是 請(qǐng)求無效闻妓,然后就執(zhí)行的查請(qǐng)求這個(gè)掠械,最夠終于找到這個(gè)bug
    strcat(request,"\r\n");        
    int rlen=strlen(request);

我把源碼貼在這里猾蒂,感興趣的可以復(fù)盤一下問題
main.c

#include "socket.c"
#include <unistd.h>
#include <sys/param.h>
#include <rpc/types.h>
#include <getopt.h>
#include <strings.h>
#include <time.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

#define REQUEST_SIZE 2048
char request[REQUEST_SIZE];   // 發(fā)送的構(gòu)造的HTTP請(qǐng)求


int main(){
    char buf[1500];

    strcpy(request,"GET / HTTP/1.0");
    strcat(request,"\r\n");
    strcat(request,"User-Agent: WebBench 1.5");
    strcat(request,"\r\n");
    strcat(request,"Host: localhost");
    strcat(request,"\r\n");
    strcat(request,"\r\n");
    int rlen=strlen(request);

    printf("----test ----- the http request is ----   : \n");
    printf("%s",request);
    printf("----end  ------\n");


    char *host="localhost";
    int port=80;
    int s=Socket(host,port);
    if(s<0){        
        printf("error \n");
        return -1;
    }
    else{
        printf("ok \n");
    }

    //write
    if(rlen!=write(s,request,rlen)){
        printf("fail \n");
        close(s);
        return -1;
    }
    printf("write len is %d",rlen);

    //read
    int i=0;
    while(1){
        i=read(s,buf,1500);
        printf("len i is : %d",i);
        if(i<0){
            printf("fail \n");
            close(s);
            return -1;
        }
        if(i==0){
            printf("%s",buf);
            printf("read comlete \n");
            break;
        }
        else{
            printf("%s",buf);
        }
    }
    close(s);
    return 0;
}

socket.c

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int Socket(const char *host, int clientPort)
{
    int sock;
    unsigned long inaddr;
    struct sockaddr_in ad;
    struct hostent *hp;
    
    memset(&ad, 0, sizeof(ad));
    ad.sin_family = AF_INET;
    
    // 將字符串轉(zhuǎn)換為32位二進(jìn)制網(wǎng)絡(luò)字節(jié)序的IPv4地址
    inaddr = inet_addr(host);
    if (inaddr != INADDR_NONE)
        memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
    else
    {
        // 使用域名或主機(jī)名獲取ip地址
        hp = gethostbyname(host);
        if (hp == NULL)
            return -1;
        memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
    }
    ad.sin_port = htons(clientPort);
    
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
        return sock;
    if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
        return -1;
    return sock;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舔箭,一起剝皮案震驚了整個(gè)濱河市蚊逢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烙荷,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戳表,死亡現(xiàn)場離奇詭異昼伴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)价涝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門持舆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泞遗,你說我怎么就攤上這事席覆。” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵晦毙,是天一觀的道長耙蔑。 經(jīng)常有香客問我,道長须揣,這世上最難降的妖魔是什么钱豁? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮卵酪,結(jié)果婚禮上谤碳,老公的妹妹穿的比我還像新娘。我一直安慰自己蜒简,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布最铁。 她就那樣靜靜地躺著垮兑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雀哨。 梳的紋絲不亂的頭發(fā)上私爷,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音捌浩,去河邊找鬼工秩。 笑死进统,一個(gè)胖子當(dāng)著我的面吹牛浪听,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迹栓,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼克伊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了答毫?” 一聲冷哼從身側(cè)響起季春,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤载弄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宇攻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘉涌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年仑最,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帆喇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡预皇,死狀恐怖婉刀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情突颊,我是刑警寧澤诱桂,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布挥等,位于F島的核電站,受9級(jí)特大地震影響堤尾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郭宝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一粘室、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衔统,春花似錦、人聲如沸舱殿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冈绊。三九已至埠啃,卻和暖如春焚碌,著一層夾襖步出監(jiān)牢的瞬間霸妹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工鹃骂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罢绽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓寝殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親市咽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)贞绳,斷路器,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • 一俱尼、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,333評(píng)論 6 152
  • 參考:http://www.2cto.com/net/201611/569006.html TCP HTTP UD...
    F麥子閱讀 2,944評(píng)論 0 14
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng)次屠,內(nèi)容來源于博客園作者M(jìn)IN飛翔的HTTP協(xié)...
    Sivin閱讀 5,210評(píng)論 3 82
  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx劫灶?那么一定聽過它的“同行”Apache吧裸违!Ngi...
    JokerW閱讀 32,646評(píng)論 24 1,002