Linux--深入解析HTTP協議
阿新 • • 發佈:2019-01-11
什麼是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地址:
結果如下: