- 服務器地址實際上是指ip地址,這裏用域名錶示,便於記憶
- http服務器端口號默認80,可以不使用80
- 帶層次的文件路徑不同,訪問服務器上的文件就不同
- 查詢字符串 : 都是以鍵值對的方式存儲的, 鍵和值之間使用等號分隔,每一組鍵值對之間用 & 分隔
- 片段標識符 : 表示要跳轉到本頁面的某一部分
即 '編碼' 和 '解碼'
- GET : http協議的方法,表示瀏覽器要從服務器上獲取某一個數據
- http://baidu.com/ : 一個url,表示具體訪問服務器上哪一個資源
- HTTP/1.1 : 當前HTTP版本號
- Host : 表示訪問主機名,也就是ip地址的別名,即域名
- Connection: 逐跳首部,連接的管理
- User-Agent : 包含操作系統信息和瀏覽器信息
- Accept: 用戶代理可處理的類型
- Accept-Encoding: 優先的內容編碼
- Accept-Language: 優先的語言(自然語言)
- Cookie : 用來保存一個從服務器返回回來的字符串,並且保存到本地,下一次訪問同一個網站,會將這個信息帶回去
- 首行 : [方法]+[url]+[版本]
- Header :
- 空行 : 表示Header結束
- Body :
- [版本號] + [狀態碼] + [狀態碼解釋]
- Sever: HTTP服務器安裝信息
- Content-Type : text/html 表示body的格式是一個xml的文本格式; charset說明字符集爲utf-8
- Content-Length : Body部分的字節數
- Vary: 代理服務器緩存的管理信息
- Location: 令客戶端重定向到指定的URL
- Set-Cookie : 和請求中的Cookie對應.服務器利用Set-Cookie向客戶端返回用來表示身份信息的的字符串,下次訪問時就會重新帶入,避免再次登錄.
- 瀏覽器見到這一部分內容,就會按照Content-Type的格式展示出來.
方法 | 說明 | 支持的HTTP協議版本 |
GET | 獲取資源 | 1.0 / 1.1 |
POST | 傳輸實體主體 | 1.0 / 1.1 |
PUT | 傳輸文件 | 1.0 / 1.1 |
HEAD | 獲得報文首部 | 1.0 / 1.1 |
DELETE | 刪除文件 | 1.0 / 1.1 |
OPTIONS | 詢問支持的方法 | 1.1 |
TRACE | 追蹤路徑 | 1.1 |
CONNECT | 要求用隧道協議連接代理 | 1.1 |
LINK | 建立和資源自檢的聯繫 | 1.0 |
UNLINK | 斷開連接關係 | 1.0 |
類別 | 原因短語 | 經常見到的狀態碼 | 描述 | |
1XX | Informational(信息性狀態碼) | 接收的請求正在處理 | ||
2XX | Success(成功狀態碼) | 請求正常處理完畢 | 200 OK | 請求成功(其後是對GET和POST請求的應答文檔。) |
3XX | Redirection(重定向狀態碼) | 需要進行附加操作以完成請求 | 302 Found | 所請求的頁面已經臨時轉移至新的url。 |
4XX | Client Error(客戶端錯誤狀態碼) | 服務器無法處理請求 | 404 Not Found | 服務器無法找到被請求的頁面。 |
5XX | Server Error(服務器錯誤狀態碼) | 服務器處理請求出錯 | 504 Gateway Timeout | 網關超時。 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
int ServerInit(short port)
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0)
{
perror("bind");
return -2;
}
ret = listen(fd,3);
if(ret < 0)
{
perror("listen");
return -3;
}
return fd;
}
void* ThreadEntry(void* arg)
{
int* pnew_sock = (int*)arg;
int new_sock = *pnew_sock;
// 構造一個足夠大的響應空間,獲取HTTP請求
char buf[1024 * 10] = {0};
read(new_sock,buf,sizeof(buf)-1);
printf("[Request] %s\n",buf);
// 構造HTTP響應
const char* first_line = "HTTP/1.1 200 OK\n"; //此處構造http響應首部
const char* blank_line = "\n"; //header和body之間的空行
const char* body_line = "<html><h1>Hello World<h1></html>"; //此處構造Body,最簡單的頁面
char header_buf[128] = {0};
//構造header,這裏僅包含body的類型和長度
sprintf(header_buf,"Content-Type: text/html;\nContent-Length: %lu\n",strlen(body_line));
write(new_sock,first_line,strlen(first_line));
write(new_sock,header_buf,strlen(header_buf));
write(new_sock,blank_line,strlen(blank_line));
write(new_sock,body_line,strlen(body_line));
close(new_sock);
return NULL;
}
int main(int argc,char* argv[])
{
if(argc != 2)
{
printf("Usage : ./http_server [port]\n");
return 1;
}
// 創建 listen_sock
int listen_sock = ServerInit(atoi(argv[1]));
if(listen_sock < 0)
{
perror("ServerInit Faild\n");
return 2;
}
printf("ServerInit OK\n");
// 進入事件循環
while(1)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
if(new_sock < 0)
{
perror("accept");
return 3;
}
// 使用線程完成多用戶可訪問模式
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,(void*)&new_sock);
pthread_detach(tid);
}
return 0;
}