Linux-C 編程 / 網絡 / 超迷你的 web server

2021-03-02 百問科技
一、為生活尋找固定的支撐點

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/tinyhttpd

Tinyhttpd 是一個 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

覺得文章對你有價值,不妨點個 在看和贊

相關焦點

  • 推薦幾款可以直接在手機上編程的app(包含Java、C、Python等)
    點擊上方藍色字體,關注我們這裡介紹幾款可以在手機上編程的
  • 玩轉嵌入式之如何在Linux系統中搭建tftp伺服器·圖文視頻
    嵌入式linux最常用到tftp命令來完成開發板和Linux系統之間的的文件傳輸,避免了頻繁的U盤拷貝過程。我們知道,Windows下使用「tftpd32.exe」這款軟體可以很方便地在Windows下搭建的tftp伺服器。
  • Linux學習之Linux系統優化進階都需要掌握哪些知識?
    一.基礎必備優化:1.關閉SElinux2.Firewalld CenetOS7 Iptables (C6) 安全組(阿里雲)3.網絡管理服務NetworkManagernetwork相同地方管理網絡管理網絡區別通過nmcli命令修改配置文件 重啟網卡4.添加普通用戶 配置sudo1oldboy ALL=(ALL) NOPASSWD: ALL5.hosts文件 解析當前的主機名
  • Devil-Linux 1.6.0 正式版發布
    Devil-Linux 1.6.0 升級的軟體包括:LVM 2.02.95, DHCP 4.2.3-P2, OpenLDAP 2.4.30, Squid 3.1.19, Webmin 1.580, Dovecot 2.1.3, MySQL 5.5.21, Samba 3.6.3, Apache HTTPD server 2.2.22, PHP 5.3.10,詳細改進記錄請看 changelog
  • 初探 Python 3 的異步 IO 編程
    在 Python 2 的時代,高性能的網絡編程主要是使用 Twisted、Tornado 和 gevent 這三個庫。我對 Twisted 不熟,只知道它的缺點是比較重,性能相對而言並不算好。Tornado 平時用得比較多,缺點是寫異步調用時特別麻煩。gevent 我只能算接觸過,缺點是不太乾淨。
  • ABB機器人OPC server
    可以去以下地址下載opc server安裝包http://developercenter.robotstudio.com/downloads_opcserver4. 下載好安裝5. 打開robotstudio仿真啟動機器人(或者pc連上真實機器人)6.
  • c定時 linux專題及常見問題 - CSDN
    1.定時任務介紹1.1 crond是什麼crond是linux系統中用來定期執行命令或指定程序的一種服務或軟體。特殊要求:(秒級別)crond服務就無法搞定了,一般工作中寫腳本用守護進程執行[root@shellbiancheng jiaobenlianxi]# cat while1.sh #!
  • k8s The connection to the server was refused 問題解決記錄
    # 輸出f40d97ee2be6 40a63db91ef8 "kube-apiserver --au…" 2 minutes ago Exited (255) 2 minutes ago k8s_kube-apiserver_kube-apiserver-master1
  • C語言之父和Linux之父誰更偉大?
    4月5號 : 第一個Linux新聞組,comp.os.linux由Ari Lemmke提議和開通。 5月21號 : Peter MacDonald 發布第一個獨立的Linux安裝包SLS。可以通過軟盤安裝,包括比較前沿的TCP-IP網絡支持和X Window系統。建議至少預留10M的磁碟空間來安裝。
  • 程式語言學哪個比較好?2019年最實用的程式語言
    學習編程關鍵是要找到一種合適的語言,那麼程式語言那麼多,該如何選擇?下面萬古網校小編為大家分享一篇關於程式語言選擇的文章,希望能給你帶來幫助!第一大類語言包括Java、C、Python和C++。這類語言都是非常通用的語言,它們並不局限於特定的編程平臺或用途。
  • 2021年比較適合用於Web開發的7種程式語言
    Python 以其許多面向 web 開發的框架而聞名,這些框架可以幫助開發人員創建功能性應用程式。最受歡迎的是 Django、Pyramid、Bottle、Flask、CherryPy、WebApp2和TurboGears。Django 比其他 Python 框架使用得更為頻繁,因為它具有讓我們在構建 Web 應用程式更快、代碼更少的工具包。
  • 在計算機軟體領域 C語言之父和Linux之父誰更偉大
    此書已翻譯成多種語言,被譽為c語言的聖經。 2011年10月12日,共事20年的同事Rob Pike從加州到新澤西去拜訪他,才發現他已經去世了。由於是獨居,無法知道準確的死亡時間。享年70歲。 4月5號 : 第一個Linux新聞組,comp.os.linux由Ari Lemmke提議和開通。 5月21號 : Peter MacDonald 發布第一個獨立的Linux安裝包SLS。可以通過軟盤安裝,包括比較前沿的TCP-IP網絡支持和X Window系統。建議至少預留10M的磁碟空間來安裝。
  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    第一個屬性為 [c]。6、套接字(sockets):這類文件通常用在網絡數據連接。可以啟動一個程序來監聽客戶端的要求,客戶端就可以通過套接字來進行數據通信。第一個屬性為 [s],最常在 /var/run目錄中看到這種文件類型。7、管道(FIFO,pipe):FIFO也是一種特殊的文件類型,它主要的目的是,解決多個程序同時存取一個文件所造成的錯誤。
  • Linux網絡配置——網絡配置文件
    第四章:Linux網絡配置4.1 Linux網絡配置文件4.1.1 ifcfg-eth0文件系統網絡配置文件在 /etc/sysconfig/network-scripts目錄下,其中ifcfg-eth0文件包含第一塊網卡的配置信息,ifcfg-eth0
  • Linux input子系統編程、分析與模板
    由於每種輸入的設備上報的事件都各有不同,所以為了應用層能夠很好識別上報的事件,內核中也為應用層封裝了標準的接口來描述一個事件,這些接口在"/include/upai/linux/input"中。 --182-->h_list是用來連結相關handle的鍊表 --183-->node用來連結其他input_dev的鍊表 分配/釋放 //drivers/input/input.c //創建一個input對象  struct input_dev *input_allocate_device(void);//釋放一個input
  • Linux 下如何創建 M3U 播放列表 | Linux 中國
    編譯自 | https://itsfoss.com/create-m3u-playlist-linux/  作者 | Shirsh 譯者 | DarkSun (lujun9972) 🌟🌟🌟🌟🌟共計翻譯:81.0 篇 貢獻時間:310 天簡介:關於如何在Linux終端中根據亂序文件創建M3U播放列表實現循序播放的小建議。
  • 推薦一個百萬級網絡框架,更是萬金油般的網絡中臺!
    一個與netty同類的網絡編程框架t-io誕生的意義?解決其它網絡框架沒有解決的疼點,如群組、用戶、Token綁定,組發,流量監控,心跳檢查、發送,IP拉黑,同步、阻塞發送,多協議適配,服務間數據共享,極易駕馭…t-io開源嗎?
  • 麻雀雖小五臟俱全|福瓏2.0迷你主機豐富軟體功能體驗視頻
    上一期,我們介紹了福瓏2.0迷你主機的外觀和接口,如果還有沒看過的小夥伴,請關注文末「龍芯中科「微信視頻號。本期視頻將重點介紹福瓏2.0迷你主機的預裝作業系統、主要應用軟體和編程開發工具。福瓏2.0迷你主機預裝作業系統是基於Fedora 28定製的龍夢F28開發者系統。