1. 程式人生 > >Linux--深入解析HTTP協議

Linux--深入解析HTTP協議

什麼是HTTP協議,HTTP協議有什麼作用?

  • 首先HTTP協議屬於應用層協議,應用層的協議就是我們 程式設計師自己定的,但是,如果每次都要我們程式設計師自己定協議,又麻煩而且容易出錯(自己什麼水平自己應該有點數( ̄_ ̄|||)),最重要的是有大佬已經定義了一些現成的(HTTP,DNS等),所以大多時候我們都是直接拿來用,而應用層的作用就是雙方達成某種約定,一端按照約定的協議傳送資料,另一端按照約定來解析資料,保證接收資料的一段可以正確解析資料,這就是應用層的作用,也是HTTP協議的作用之一。

HTTP協議的格式

HTTP協議分為HTTP請求和HTTP響應

HTTP請求:
這裡寫圖片描述
說明:

  • 第一部分為第一行,為請求行,請求行以空格分為三個區域,第一個區域叫請求方法,常見方法為POST,GET方法;第二個區域指名想要訪問的資源(通常以路徑的形式呈現)(網頁也屬於檔案);第三個區域表示HTTP協議行的版本(常見1.0/1.1).
  • 第二部分從第二行開始,一直到空前叫請求報頭(Header),每一行都代表一個特定的含義,每一行格式都是name:value。
  • 第三部分為空行,目的是為了將請求正文和請求行、請求報頭分隔開(讀到空格表示請求行和請求報頭讀完)。
  • 第四部分問請求正文(Body可以為空,請求報頭中的content-length表示正文大小)

HTTP響應:
這裡寫圖片描述
說明:

  • 第一部分為第一行,為響應行,也分為三個區域,用空格間隔,第一個區域表示協議版本;第二個區域表示狀態碼(200–表示請求正常處理完畢、404–表示無法處理請求等常見狀態碼);第三個區域表示狀態碼解釋。
  • 第二部分從第二行開始,一直到空前叫請求報頭,每一行都代表一個特定的含義,每一行格式都是name:value。
  • 第三部分為空行,目的是為了將請求正文和請求行、請求報頭分隔開(讀到空格表示請求行和請求報頭讀完)。
  • 第四部分為正文(Body),空⾏後⾯的內容都是Body. Body允許為空字串. 如果Body存在, 則在Header中會有⼀個Content-Length屬性來標識Body的⻓度。

常見狀態碼介紹:
這裡寫圖片描述

實現一個簡單的HTTP伺服器

#include <sys/wait.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> void service(int sock, char* ip, int port) { char buf[10240];//用一個足夠大的緩衝區一次性將資料讀完 buf[0] = 0; read(sock, buf, sizeof(buf)); const char* response = "HTTP/1.0 200 OK\n\n<html><head>hello world</head></html>\n"; write(sock, response, strlen(response)); } int StartUp(int port) { int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) { perror("socket"); exit(2); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_port = htons(port); if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) { perror("bind"); exit(3); } //設定為監聽狀態 if(listen(sock, 5) < 0) { perror("listen"); exit(4); } return sock; } // ./myHttp 0 9090 int main(int argc, char* argv[]) { if(argc != 3) { printf("Usage: %s [ip] [port]\n", argv[0]); exit(1); } int listen_sock = StartUp(atoi(argv[2])); struct sockaddr_in peer; int len = sizeof(peer); socklen_t new_sock = -1; daemon(0, 0); while(1) { if((new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len)) < 0) { printf("Connect faily\n"); continue; } char ipBuf[24] = {0}; inet_ntop(AF_INET, (struct sockaddr*)&peer, ipBuf, sizeof(ipBuf)); int p = ntohs(peer.sin_port); printf("get a new connect: [%s %d]\n", ipBuf, p); int id = fork(); if(id == 0)//child { close(listen_sock); if(fork() > 0) { close(new_sock); exit(0); } service(new_sock, ipBuf, p); close(new_sock); exit(0); } else if(id > 0)//father { close(new_sock);//因為父程序不通訊 waitpid(id, NULL, 0); continue; } else { perror("fork"); continue; } } }

測試: centos 7

首先關閉防火牆(臨時關閉):

//臨時關閉
systemctl stop firewalld
//禁止開機啟動
systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.

執行伺服器
這裡寫圖片描述
在瀏覽器上輸入自己的ip地址:
這裡寫圖片描述
結果如下:
這裡寫圖片描述