HTTP協議學習


    面試過程中又一個常見的問題,http協議,因為做服務器開發如果用http協議的話,現在各種開源軟件都封裝好了,python中只需要簡單的繼承定義好的類,重寫get或者post等方法,幾行代碼就可以搭建起來一個簡單的http服務器,導致底層對程序員來說都是透明的了。但是面試中追求這個底層的問題還不少,所以最近入手了一本據說一天入門http協議的書籍《圖解http》閱讀一番,才覺http協議原來是這個樣子~這里總結一下自己的學習心得吧。

    現在理解http協議其實只是一些規定好的通信標准,其實發送的均為字符串,去處理字符串即可。(不知道這句話能不能理解,后面會再次解釋一下)。

    HTTP協議在TCP/IP協議棧中屬於應用層協議,如果基於SSL或者TSL,這個協議就演變為更加安全的HTTPS協議。默認HTTP端口號80,HTTPS默認端口號443。

    與HTTP協議協同工作的重要協議DNS,TCP,IP等協議,其中DNS同為應用層協議。

    

    HTTP協議規定,請求從客戶端發出,最后服務器端響應該請求並返回。這是目前HTTP協議的規定,服務器不支持主動響應,所以目前的HTTP協議版本都是基於請求,然后響應的這種模型。

    

    另外,HTTP協議是一種無狀態的應用層協議,即使同一個客戶端的兩次連續請求,在協議規定中也沒有對應關系。所以為了解決無狀態的這個問題,存在其他的技術解決方案進行補充。

HTTP請求報文和響應報文:

    請求報文: 請求方法 , 請求URI , 協議版本 , 可選的請求首部字段和內容實體。(嚴格定義的字符串)

   

    響應報文:協議版本 , 狀態碼 , 用以解釋狀態碼的原因短語 , 可選的響應首部字段以及實體主體。(嚴格定義的字符串)

    

    以上兩個截圖自己利用socket寫了一個簡單的服務端和客戶端程序,分別用瀏覽器發送請求和向百度發送請求得到的字符串打印。HTTP具體還是基於TCP協議的,所以各種HTTP服務器底層應該是同樣的機制,不過它們處理了很多關於HTTP協議處理的部分,這部分更多的感覺是協議規定好的字符串的內容,各種方法,各種頭的屬性。

HTTP請求,響應的模擬

    另外除了自己利用socket去編輯,更簡單觀察相應的方法可以利用telnet去發送http請求頭至百度。

        telenet 開啟方法 控制面板-程序-window程序開啟與關閉-開啟telnet客戶端,這樣就能夠在cmd中使用telnet命令了。使用起來不是很方便。

    異或是利用wireshark直接去抓取你訪問某個網站整個過程的數據包也可以觀察~。以下是抓取的請求www.baidu.com的數據包。

    

 

    整個請求,響應過程包過濾后如下:

    

   其中可以可到tcp的三次握手,以及請求,響應的過程。但是其中有幾個包內容是TCP segment of a reassembled PDU不是很清楚,可以看到,tcp每次發送均有響應的ACK回應。具體的TCP得參考TCP協議的知識。

   另外HTTP1.0和HTTP1.1的有一個主要區別就是HTTP1.1引入了持續連接的概念,不然完成一次請求必然導致TCP三次握手成本太高,引入持續連接的概念提高傳輸的效率。

HTTP請求支持的方法

GET   請求獲取Request-URI所標識的資源

POST 在Request-URI所標識的資源后附加新的數據

HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭

PUT    請求服務器存儲一個資源,並用Request-URI作為其標識

DELETE 請求服務器刪除Request-URI所標識的資源

OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求

TRACE   請求服務器回送收到的請求信息,主要用於測試或診斷

CONNECT 隧道機制

HTTP的狀態響應碼

1XX:指示信息,請求收到,繼續處理

2XX:成功,操作成功收到,分析,接受

3XX:完成請求必須進一步處理,重定向

4XX:請求包含一個錯誤語法,不能完成。指示客戶端錯誤

5XX:服務器執行一個有效請求失敗,指示服務端錯誤

 

其他HTTP重要的知識就是報頭信息中的具體字段信息:

報頭字段的格式為 (名字)“:”(空格)(值);

通用報頭字段:

Cache-Control

Date

Connection

請求報頭字段:

Accept

Accept-Charset

Accept-Encoding

Accept-Language

Authorization

Host

User-Agent

響應報頭字段:

Location

Server

實體報頭字段:

Content-Encoding

Content-Language

Content-Length

Content-Type

Last-Modified

Expires

 

最后貼出自己利用winsock實現的測試http內容的簡單程序:

實現流程圖如下:

    

//TCP 客戶端
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <string>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define PORT 8080
#define BACKLOG 10
int main(void) {
	//client module	
	WSADATA r_wsadata;
	if( WSAStartup(MAKEWORD(2,2),&r_wsadata) ) {
		cout<<"WSAStartup error"<<endl;
		WSACleanup();
	}
	SOCKET client_s;
	if( (client_s = socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET ) {
		cout<<"socket error"<<endl;
		WSACleanup();
	}
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(80);
	server_addr.sin_addr.s_addr = inet_addr("202.108.22.5");
	if( connect(client_s,(sockaddr *)&server_addr,sizeof(server_addr)) ) {
		cout<<"connect error"<<endl;
		WSACleanup();
	}
	char *sbuf ="GET / HTTP/1.1\r\nHost:www.baidu.com\r\nUser-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2\r\nAccept-Language: zh-cn,zh;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection:Keep-Alive\r\n\r\n";
	char *sbuf1 ="GET / HTTP/1.1\r\nHost:www.baidu.com\r\nUser-Agent:Mozilla/5.0 (Windows;U;Windows NT 5.1; en-US; rv:1.7.6)\r\nGecko/20050225 Firefox/1.0.1\r\nConnection: Keep Alive\r\n\r\n";
	int len = strlen(sbuf) + 1;
	int sendlen = 0;
	int res = send(client_s,sbuf,len,0);
	char buf[10240];
	int recvlen = recv(client_s,buf,sizeof(buf),0);
	if(recvlen > 0)
	    buf[recvlen] = '\0';
	cout<<string(buf)<<endl;
	system("pause");
}
//TCP
//服務器端程序
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <string>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define PORT 8080
#define BACKLOG 10
int main(void) {
	
	//server module 
	WSADATA r_wsadata;
	if( WSAStartup(MAKEWORD(2,2),&r_wsadata) ) {
		cout<<"WSAStartup error"<<endl;
		WSACleanup();
		exit(0);
	}

	SOCKET server_s;
	if( ( server_s = socket(AF_INET,SOCK_STREAM,0) ) == INVALID_SOCKET ) {
		cout<<"socket error"<<endl;
		WSACleanup();
		exit(0);
	}
	sockaddr_in server_s_addr;
	server_s_addr.sin_family = AF_INET;
	server_s_addr.sin_port = htons(PORT);
	server_s_addr.sin_addr.s_addr = INADDR_ANY;
	if( bind(server_s,(sockaddr *)&server_s_addr,sizeof(server_s_addr)) ) {
		cout<<"bind error"<<endl;
		WSACleanup();
		exit(0);
	}
	if( listen(server_s,BACKLOG) ) {
		cout<<"listen error"<<endl;
		WSACleanup();
		exit(0);
	}
	while(true) {	
		SOCKET accept_socket;
		sockaddr_in client_s_addr;
		int addr_len = sizeof(client_s_addr);
		if( (accept_socket = accept(server_s,(sockaddr *)&client_s_addr,&addr_len)) == -1 ) {
			cout<<"accept error"<<endl;
			WSACleanup();
			exit(0);
		}
		cout<<"request from host:"<<string( inet_ntoa(client_s_addr.sin_addr) )<<endl;
		cout<<"-----------------------http request----------------------"<<endl;
		//receive
		char buf[1024];
		int readLen = -1;
		while(readLen < 0)
		    readLen = recv(accept_socket,buf,sizeof(buf),0);
		buf[readLen] = '\0';
		cout<<string(buf)<<endl;
		closesocket(accept_socket);
	}
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM