1. 程式人生 > >應用層(http協議) http與https區別

應用層(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伺服器利用會話金鑰加密與客戶端之間的通訊。