1. 什麼是生活的支撐點?
讓自己感到些許痛苦,但卻會帶來實實在在的充實感和成就感的事情。
2. 固定的支撐點很重要:
無論發生什麼,無論今天你在什麼地方,處於怎樣的階段,有哪些安排,你都堅持做這件事,而且一大早起來,就開始執行。
這是一個很強大的心理暗示:只要擁有這些穩定不變的支撐點,你就有信心和足夠的能力去面對許多生活裡不穩定的東西,並解決掉它們。
二、Linux-C 編程 / 超迷你的 web server0. 什麼是 web server?web server 有兩個意思:
一臺負責提供網頁的主機,它通過 http 協議將網頁等數據傳給客戶端(一般是瀏覽器);
一個提供網頁的伺服器程序,例如 Apache / Nginix / lighttped 等;
1. Tinyhttpd 簡介開源項目 Tinyhttpd ( 6K star / 2.8K fork):
官網:
https://sourceforge.net/projects/tinyhttpd/github mirror:
https://github.com/EZLippi/Tinyhttpd中文注釋代碼:
https://github.com/cbsheng/tinyhttpdTinyhttpd 是一個 C 語言編寫、極度簡陋的 web 伺服器,也可以叫 http 伺服器。
它的作用僅僅是用於學習 http 協議和 UNIX 系統調用, 不能用於生產環境中。
雖然它沒有任何商業價值,但是非常適合用來了解 WEB 伺服器的基礎知識。
我們可以用它作為我們學習 Mpjg-streamer / Nginx/ Lighttpd 等更複雜和更優秀開源項目的跳板。
2. 編譯運行編譯運行:
$ git clone https://github.com/EZLippi/Tinyhttpd
$ cd tinyhttpd-0.1.0
$ make
$ ./httpd
httpd running on port 4000用瀏覽器訪問:
點擊查看大圖用命令訪問:
使用 netstat 查看 tinyhttpd 的網絡狀態:$ netstat -ant | grep 4000
tcp 0 0 0.0.0.0:4000 0.0.0.0:* LISTEN
使用 nc 連接 tinyhttpd,並手動發送 http 請求:$ nc 127.0.0.1 4000
GET /index.html HTTP/1.1 // 輸入 http 請求
HTTP/1.0 200 OK // 接收到 http 響應
Server: es-hacker-httpserver
Content-Type: text/html
<HTML>
<TITLE>Index</TITLE>
<BODY>
<H1>This is a simple webserver
</BODY>
</HTML>
使用 curl 給 tinyhttpd 發送 http 請求:$ curl localhost:4000/index.html
<HTML>
<TITLE>Index</TITLE>
<BODY>
<H1>This is a simple webserver
</BODY>
</HTML>使用 wireshark 抓包:
點擊查看大圖(1) 瀏覽器:「請給我 ××× 網頁的數據。」
(2) web 伺服器:「好的,這就是你要的數據。」
3. 了解一下內部實現3.1 關於 web server 的入門知識web server 和 http 協議在整個網絡傳輸中的位置:
點擊查看大圖web server 處理請求的步驟:
詳細一點的步驟:
點擊查看大圖接收請求——從網絡中讀取一條 http 請求報文;構建響應——創建帶有正確首部的 http 響應報文;什麼是 http 報文?
http 報文是符合 http 協議的文本數據塊;
請求行中的方法欄位:
http 協議定義了客戶端和伺服器之間交互的消息內容和步驟,其基本思路非常簡單。
首先,客戶端會向伺服器發送請求消息請求消息中包含的內容是 "對什麼" 和 "進行怎樣的操作" 兩個部分。
其中 "對什麼" 的部分就是 URI (Uniform Resource Identifier,統一資源標識符),一般就是網頁或者文件或者程序等,而 "進行怎樣的操作" 的部分稱為方法,包括:
點擊查看大圖3.2 Tinyhttpd 的內部實現分解 httpd.c:
void accept_request(void *arg)
void bad_request(int client)
void cat(int client, FILE *resource)
void cannot_execute(int client)
void error_die(const char *sc)
void execute_cgi(int client, const char *path,
int get_line(int sock, char *buf, int size)
void headers(int client, const char *filename)
void not_found(int client)
void serve_file(int client, const char *filename)
int startup(u_short *port)
void unimplemented(int client)
int main(void)就13 個 函數,和一些宏定義,就沒有其他內容了。
程序入口:main()
int main(void)
{
int client_sock = -1;
pthread_t newthread;
// 1. 創建 socket,並且等待連接
int server_sock = startup(&port);
while (1) {
// 2. 接受連接
client_sock = accept();
// 3. 創建處理線程
pthread_create(&newthread ,
NULL,
(void *)accept_request,
...)
}
}創建 socket: startup()
int startup(u_short *port)
{
struct sockaddr_in name;
// 1. 創建 webserver 端的 socket
httpd = socket(PF_INET, SOCK_STREAM, 0);
// 2. 初始化 webserver 的 ip 地址
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
// 3. 綁定 webserver 的socket 和 ip 地址
bind(httpd, (struct sockaddr *)&name, ...);
// 4. 開始監聽
listen(httpd, 5);
}沒什麼特別的,就是典型 tcp server 編程:
解析 http 請求報文:accept_request()
這裡將會完成 web server 最核心的工作:
void accept_request(void *arg)
{
// 1. 提取第一行數據
numchars = get_line(client, buf, sizeof(buf));
// 2. 從第一行數據中提取出 http 方法
// 3. 處理 POST 方法
// 4. 處理 GET 方法
if (strcasecmp(method, "GET") == 0) {
// 4.1 提取 URI
while (...)
query_string++;
}
// 5. 構建並發送 http 響應報文給客戶端
serve_file(client, path);
}構建並發送 http 響應報文給客戶端:serve_file()
void serve_file(int client, const char *filename)
{
// 1. 打開 URI 指定的資源
FILE *resource = fopen(filename, "r");
// 2. 發送 響應報文的 header: HTTP/1.0 200 OK...
headers(client, filename);
// 3. 讀取並發送資源給 客戶端: fgets() ---> send()
cat(client, resource);
}到此,Tinyhttpd的核心實現就分析完了,更多的細節,請各位自行閱讀源碼吧~~~
4. 相關參考Lighttpd Documentation (https://redmine.lighttpd.net/projects/lighttpd/wiki/docs)
三、思考技術,也思考人生要學習技術,更要學習如何生活。
你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。
對 嵌入式系統 (Linux、RTOS、OpenWrt、Android) 和 開源軟體 感興趣,關注公眾號:嵌入式Hacker。
覺得文章對你有價值,不妨點個 在看和贊。