應用層(http協議) http與https區別
在協議分層的TCP/IP(或四層)通訊協議採用了5層的層級結構,5層分別包括:應用層、傳輸層、網路層、資料鏈路層、物理層。5層一些簡單功能和著名協議可參考這篇部落格:https://blog.csdn.net/sophia__yu/article/details/82717115
一.應用層
程式設計師寫的一些網路程式都在應用層。應用層負責應用程式間的資料溝通。
網路通訊協議:網路資料傳輸中資料格式的約定
自定製協議:程式設計師自己設定的資料傳輸格式(下面網路加法器的定義的結構體)
知名協議:http協議
如果要實現一個網路加法器,客戶端需要把計算的兩個加數發過去,服務端再將進行計算,然後將計算結果返回給客戶端。
對於網路加法器有兩種方案:
方案一:(網路通訊協議)
客戶端傳送⼀一個形如"1+1"的字串;
這個字串中有兩個運算元, 都是整形;
兩個數字之間會有⼀一個字元是運算子, 運算子只能是 + ; 數字和運算子之間沒有空格;
方案二(自定製協議)
定義結構體來表⽰示我們需要互動的資訊;
傳送資料時將這個結構體按照一個規則轉換成字串, 接收到資料的時候再按照相同的規則把字元 串轉化回結構體; 或者也可以不是字串,只要客戶端和服務端約定好接收和傳送資料的型別即可;
這個過程叫做 “序列化” 和 “反序列化”
序列化:將結構化資料/物件轉換成記憶體中的二進位制字串;
反序列化:將記憶體中的二進位制字串轉換成結構化資料/物件。
//客戶端簡單計算器程式碼 //客戶端傳輸一個格式化的資料(兩個數字和運算子)到伺服器, //傳輸的格式化資料用結構體表示 #include<stdio.h> #include<errno.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> typedef struct cal_req { int x; int y; unsigned char sym; //操作符 }cal_req; int main(int argc,char* argv[]) { if(argc!=3) { printf("Usage ./ ip port\n"); } //1.建立套接字 int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sockfd<0) { perror("socket error"); return -1; } //2.繫結地址資訊,但不推薦手動繫結資訊 //3.向服務端發起連線請求 struct sockaddr_in ser_addr; ser_addr.sin_family=AF_INET; ser_addr.sin_port=(htons)(atoi(argv[2])); ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);//因為argv[]是char*,用atoi使字串轉成整型 int len=sizeof(struct sockaddr_in); int ret=connect(sockfd,(struct sockaddr*)&ser_addr,len); if(ret<0) { perror("connect error"); close(sockfd); return -1; } //連線成功,socket描述符裡有服務端和客戶端IP地址和port //傳送加法的資料和運算子 cal_req req; req.x=1; req.y=2; req.sym='+'; len=sizeof(cal_req); ret=send(sockfd,(void*)&req,len,0); if(ret<0) { perror("send error"); close(sockfd); return -1; } //接收運算結果 int sum=0; recv(sockfd,(void*)&sum,sizeof(int),0); printf("sum:%d\n",sum); close(sockfd); return 0; }
//網路版計算器,需要傳輸兩個資料和一個運算子 //自定製協議: //前四個位元組是第一個資料 //中間四個位元組是第二個資料 //最後一個位元組是運算子 //為了對資料協議更加容易組織和解析,所以用結構化資料 //1.建立套接字 //2.繫結地址資訊 //3.開始監聽 //4.接受客戶端連線 //5.接受客戶端傳送來的資料 //6.將接受的資料進行解析 //7.返回結果 //8.關閉描述符 #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> //listen 從close狀態轉換為監聽狀態 typedef struct cla_req { int x; int y; unsigned char sym; }cla_req; int main(int argc,char* argv[]) { if(argc!=3) { printf("Usage:./ ip port\n"); return -1; } //建立套接字 int sockfd; sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sockfd<0) { perror("socket error"); return -1; } //2.繫結地址資訊 struct sockaddr_in lis_addr; lis_addr.sin_family=AF_INET; lis_addr.sin_port=htons(atoi(argv[2])); lis_addr.sin_addr.s_addr=inet_addr(argv[1]); int len=sizeof(struct sockaddr_in); int ret=bind(sockfd,(struct sockaddr*)&lis_addr,len); if(ret<0) { perror("bind error"); close(sockfd); return -1; } //3.監聽,連線成功佇列最多有個結點 if(listen(sockfd,5)<0) { perror("listen error"); close(sockfd); return -1; } while(1) { //4.獲取新連線的客戶端 struct sockaddr_in cli_addr; len=sizeof(struct sockaddr_in); int newsockfd; newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len); if(newsockfd<0) { perror("accpet error"); close(newsockfd); continue; } //5.接受客戶端的資料 cla_req req; len=sizeof(req); ret=recv(newsockfd,(void*)&req,len,0); if(ret<0) { perror("recv error"); close(newsockfd); continue; } else if(ret==0) { perror("peer shutdown"); close(newsockfd); continue; } int sum=0; if(req.sym=='+') { sum=req.x+req.y; } //6.將計算結果返回給客戶端 ret=send(newsockfd,(void*)&sum,sizeof(int),0); if(ret<0) { perror("send error"); close(newsockfd); return -1; } close(newsockfd); } close(sockfd); return 0; }
只要保證, 一端傳送時構造的資料, 在另一端能夠正確的進行解析, 就可以。 這種約定, 就是 應用層協議。
http協議
http協議(超文字傳輸協議)是應用層知名協議。
URL是平常所說的網址。
埠號如果沒有指定則預設是80,(為什麼說埠號0-65535之間一個數字,但0-1024不推薦使用,是因為0-1024被一些知名協議所佔用,如80被知名協議http佔用);
/是相對根目錄;
查詢字串在標準程式碼稱QUERY_STRING
ch1:直接跳轉某個網頁的那個標籤;
URL編碼:
為什麼存在URL編碼:
因為在URL中特殊字元在很多地方有特殊含義,因此為了防止提交的資料也包含特殊字元造成歧義,因此對提交的資料進行URL編碼操作.
URL編碼是什麼?
url編碼(查詢字串):將特殊字元高四位轉換成16進位制數字,低四位轉換成16進位制數字,為了告訴對方這是已經編碼過的字元,因此在轉義後的兩個16進位制字元前加上%;
URL編碼過程:
比如:特殊字元’+’:
Http協議格式(Http資料結構)
http是一個明文字串傳輸。
http請求:
http請求協議格式分為四個部分:首行、協議頭、空行、正文。
首行:[請求方法]+[URL]+[協議版本]
比如:GET https:: //www.baidu.com/ HTTP/1.1。
請求方法包括:GET和POST。
GET和POST區別和聯絡:
聯絡:GET和POST都是為了獲取資源或者提交資源。
區別:
GET :更多是為了獲取資源,但也可以提交資源,但是提交資源的URL長度有限(限制早期是IK,現在nginx伺服器預設的限制是Ik或者8K,這是根據伺服器的硬體配置有關,一般為記憶體一頁的大小,目前大部分為4K,即4096位元組。)
POST:更多用於提交表單資料,也可以獲取資源,提交的資料是在正文中,正文的長度沒有限制。
總結:GET將資料傳送到URL,POST將資料傳送在正文中,兩者區別是有無正文。
協議頭:
請求的屬性,冒號分割的鍵值對;每組屬性之間用\r\n分割;遇到空行則表示Header部分結束。下文會有具體的協議頭。
空行:
空行其實是協議頭和正文之間一行,標識協議頭結束,正文開始。
**正文:**空行後面的內容都是正文,正文允許為空字串,如果正文存在,則在協議頭中會有一個Content-Length屬性來標識正文的長度。
整體如下:首行 \r\n協議頭1\r\n協議頭2\r\n\r\n正文
http響應:
http響應協議格式包括四部分:首行、協議頭、空行、正文。
首行:[版本號]+[狀態碼]+[狀態碼解釋];
比如:HTTP/1.1 302 Moved Temporarily
協議頭:
請求的屬性,冒號分割的鍵值對;每組屬性之間用\r\n分割;遇到空行則表示Header部分結束。下文會有具體的協議頭。
空行:
空行其實是協議頭和正文之間一行,標識協議頭結束,正文開始。
正文:
空行後面的內容都是正文,正文允許為空字串,如果正文存在,則在協議頭中會有一個Content-Length屬性來標識正文的長度;如果伺服器返回一個html頁面,那麼html頁面內容就是在正文中。
HTTP狀態碼:
最常見的狀態碼:200(OK),404(NOT FOUND),403(Forbidden),302(Redirect,重定向),504(Bad Gateway)
實現一個十分簡單的HTTP伺服器,只在網頁上輸出"hello world",只要我們按照HTTP協議格式的要求構造資料,就很容易能做到:
//十分簡單的http服務端程式,它可以接收瀏覽器的請求,但是,不管什麼請求,都只回復同一個資源
//<hl>hello world</hl>
//http是應用層協議,在傳輸層使用的是tcp協議,所以我們要寫的是http服務端程式實際上是tcp服務端程式
//假設我們現在http服務端程式監聽的是10000埠,也就意味著瀏覽器請求的是時候url中伺服器地址也需要手動指定為
//10000,否則瀏覽器預設使用80埠
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
//整體的http響應:
//首行 HTTP/1.1 200 OK
// 頭資訊
// Content-Length:
// Content-Type:text/html; charset=UTF-8
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0)
{
perror("sockfd error");
return -1;
}
struct sockaddr_in lis_addr;
lis_addr.sin_family=AF_INET;
lis_addr.sin_port=htons(9001);
lis_addr.sin_addr.s_addr=(inet_addr)("192.168.61.128");
int len=sizeof(struct sockaddr_in);
int ret=bind(sockfd,(struct sockaddr*)&lis_addr,len);
if(ret<0)
{
perror("bind error");
return -1;
}
if(listen(sockfd,5)<0)
{
perror("listen error");
return -1;
}
while(1)
{
struct sockaddr_in cli_addr;
len=sizeof(struct sockaddr_in);
int newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);
if(newsockfd<0)
{
perror("accept error");
continue;
}
char buff[1024]={0};
//接收資料
ret=recv(newsockfd,buff,1023,0);
if(ret>0)
{
printf("req:[%s]\n",buff);
}
else
{
perror("recv error");
close(newsockfd);
continue;
}
memset(buff,0x00,1024);
char* str="<h1>hello world</h1>";
//sprintf(buff,"%s\r\n%s%d\r\n%s\r\n%s\r\n\r\n%s","HTTP/1.1 302 FOUND","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8","Location: http://www.baidu.com/",str);
//sprintf(buff,"%s\r\n%s%d\r\n%s\r\n\r\n%s","HTTP/1.1 404 NOT FOUND","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8",str);
sprintf(buff,"%s\r\n%s%d\r\n%s\r\n\r\n%s","HTTP/1.1 200 OK","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8",str);
printf("%s\n",buff);
//sprintf(buff,"HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n%s",strlen(str),str);
send(newsockfd,buff,strlen(buff),0);
close(newsockfd);
}
close(sockfd);
return 0;
}
注:一般在執行時,需要關閉防火牆:service iptables stop(臨時關閉防護牆)
如果狀態碼是200,結果如下:
如果狀態碼是302,即重定向為百度,需要和Location一起使用:
如果是404(NOT FOUND),結果如下:
HTTP常見協議頭:
User-Agent :作業系統資訊,根據不同的作業系統,返回不同的頁面
Cookie :使用者認證登入資訊
Referer :從 哪一個頁面跳轉
Content-Type:資料型別(text/html等)
Content-Length :當前正文長度(字元)
Location:搭配3xx狀態碼使用,告訴客戶端接下來去哪兒訪問。
http和https區別:
HTTP協議傳輸的資料都是未加密的,也就是明文的,因此使用HTTP協議傳輸隱私資訊非常不安全,為了保證這些隱私資料能加密傳輸,於是網景公司設計了SSL(Secure Sockets Layer)協議用於對HTTP協議傳輸的資料進行加密,從而就誕生了HTTPS。簡單來說,HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,要比http協議安全。
區別:
1、https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
2、http是超文字傳輸協議,資訊是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
3、http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443。
4、http的連線很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。
客戶端在使用HTTPS方式與Web伺服器通訊時有以下幾個步驟,如圖所示。
(1)客戶使用https的URL訪問Web伺服器,要求與Web伺服器建立SSL連線。
(2)Web伺服器收到客戶端請求後,會將網站的證書資訊(證書中包含公鑰)傳送一份給客戶端。
(3)客戶端的瀏覽器與Web伺服器開始協商SSL連線的安全等級,也就是資訊加密的等級。
(4)客戶端的瀏覽器根據雙方同意的安全等級,建立會話金鑰,然後利用網站的公鑰將會話金鑰加密,並傳送給網站。
(5)Web伺服器利用自己的私鑰解密出會話金鑰。
(6)Web伺服器利用會話金鑰加密與客戶端之間的通訊。