Mongoose是一個開源web服務器程序,僅有mongoose.c和 mongoose.h兩個文件,雖然小巧,但具有基本的web服務器的功能,同時Mongoose是用C編寫的,所以很是適合應用到嵌入式設備中。
目前Mongoose能夠支持嵌入到C/C++、Python、C#。本文主要介紹下如何在Linux環境下將Mongoose代碼嵌入到C應用程序中,並完成服務器的部署工做。
爲了更快地熟悉web服務器的特性,在介紹部署服務器以前,有必要先介紹下服務器和客戶端之間網絡通信的基礎傳輸技術。php
瀏覽器與Web服務器之間的請求和響應是使用HTTP協議進行的,但HTTP協議只是應用層使用的協議,傳輸層進行的數據通訊則是經過TCP協議來保證的。
TCP協議是面向鏈接的協議,整體包括創建鏈接、數據傳輸和關閉鏈接這三個過程,創建鏈接採用「三次握手」方式來完成,鏈接成功後便可發送數據,在關閉鏈接以前,爲確保數據正確傳遞完畢,須要採用「四次揮手」方式關閉鏈接。下面來分析下「三次握手」和「四次揮手」的過程。html
一、客戶端(192.20.1.12)「三次握手」鏈接服務器(192.20.1.122)
程序員
SYN(flag)=1,表示客戶端要求創建鏈接
Seq=x,隨機產生序號
SYN(flag)=1
ACK(flag)=1,表示服務器確認請求
ACK(num) =x+1(客戶端Seq+1)
Seq=y,隨機產生序號
ACK(flag)=1,檢查正確,客戶端發送確認
ACK(num) =y+1(服務器Seq+1),客戶端發送確認號
Seq=x+1
二、客戶端(192.20.1.12)與服務器(192.20.1.122)「四次揮手」斷開鏈接。客戶端與服務器都可主動發起斷開TCP鏈接請求,此處以客戶端發起請求爲例。
web
FIN(flag)=1,客戶端要求終止鏈接
ACK(num)=z,上包數據Seq值
Seq=x,上包數據ACK(num)值
ACK(flag)=1,表示服務器確認請求
ACK(num) =x+1(客戶端Seq+1)
Seq=y,隨機產生序號
FIN(flag)=1,服務器要求終止鏈接
ACK(flag)=1,服務器發送確認
ACK(num) =x+1,與第二次揮手一致
Seq=y,與第二次揮手一致
ACK(flag)=1,檢查正確,客戶端發送確認
ACK(num) =y+1(服務器Seq+1),客戶端發送確認號
Seq=x+1
也許有人會有疑惑,爲何創建鏈接協議是「三次握手」,而關閉鏈接倒是「四次揮手」呢?因爲TCP鏈接是全雙工的,當關閉鏈接時,服務器收到客戶端的FIN報文通知,僅僅表示客戶端沒有數據發送給服務器,但未必服務器全部的數據都發送給了客戶端,可能還須要發送一些數據後再關閉鏈接。因此ACK報文和FIN報文分開發送造成了「四次揮手」。shell
在網絡編程中不可避免地會使用到Socket編程,Socket將TCP/IP協議封裝成API接口,
使得程序員更方便地使用TCP/IP協議棧。
Socket是以"打開—讀/寫—關閉"模式實現,以TCP協議通信的Socket爲例,交互流程大體以下,這裏從服務器的角度對流程進行介紹:編程
C程序的編譯過程當中,依次要進行預處理、編譯、彙編、連接四個階段,在Linux平臺shell輸入gcc命令完成上述步驟,但gcc在編譯一個包含許多源文件的工程時,須要將其中的每一個源文件都編譯一遍,而後再所有鏈接起來,這樣效率很是低,尤爲當用戶只是修改了其中某一個文件的時候,許多已經生成的目標文件是不會改變的,徹底沒有必要將每一個文件都從新編譯一遍。
爲解決gcc編譯的低效問題,Windows平臺上的集成開發環境(IDE)提供了工程管理器,用戶只須要點擊一個「make」按鈕就能夠啓動工程管理器對整個程序進行自動編譯。
在Linux平臺上,make工具和makefile提供了簡單有效的工程管理方式。makefile決定編譯工程的規則,經過make命令能夠啓動make工具根據makefile文件中的編譯規對程序進行編譯和連接,最終生成可執行文件。
Mongoose官網下載的源程序例程中提供了makefile文件,能夠直接使用。瀏覽器
文件一: PROG = simplest_web_server MODULE_CFLAGS=-DMG_DISABLE_DAV_AUTH -DMG_ENABLE_FAKE_DAVLOCK include ../examples.mk 文件二: SUBDIRS = $(sort $(dir $(wildcard ./*/))) SUBDIRS:=$(filter-out ./ ./CC3200/ ./ESP32_IDF/ ./ESP8266_RTOS/ ./mbed/ ./MSP432/ ./nRF51/ ./nRF52/ ./NXP_K64/ ./NXP_LPC4088/ ./PIC32/ ./STM32F4_CC3100/ ./TM4C129/ ./WinCE/, $(SUBDIRS)) ifeq ($(OS), Windows_NT) SUBDIRS:=$(filter-out ./netcat/ ./raspberry_pi_mjpeg_led/ ./captive_dns_server/, $(SUBDIRS)) endif .PHONY: $(SUBDIRS) all: $(SUBDIRS) $(SUBDIRS): @$(MAKE) -C $@ clean: for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
此處爲了更好地說明makefile文件的功能,簡單地編寫了生成可執行文件的編譯規則。將simplest_web_server.c mongoose.h編譯成中間文件simplest_web_server.o,mongoose.c mongoose.h編譯成中間文件mongoose.o,再將.o文件文件和所需的庫文件連接成最終的可執行文件.all服務器
all : simplest_web_server.o mongoose.o gcc -o all simplest_web_server.o mongoose.o simplest_web_server.o : simplest_web_server.c mongoose.h gcc -c simplest_web_server.c mongoose.o : mongoose.c mongoose.h gcc -c mongoose.c
Mongoose開源程序(官網下載6.9版本)提供了多個功能的例程,這裏選用simplest_web_server(基礎web服務器程序)的main函數做爲主應用程序。網絡
建立Socket、綁定、監聽等流程在如下函數中實現:socket
nc = mg_bind(&mgr, s_http_port, ev_handler);
循環mg_mgr_poll()遍歷全部Socket,接受到新鏈接後進行數據收發,同時調用Socket相應事件處理函數。
for (;;) { mg_mgr_poll(&mgr, 1000); }
由用戶實現的事件處理接口以下所示,可在該接口中實現服務器後臺的業務邏輯功能:
static void ev_handler(struct mg_connection *nc, int ev, void *p) { if (ev == MG_EV_HTTP_REQUEST) { mg_serve_http(nc, (struct http_message *) p, s_http_server_opts); } }
此處示例在mg_serve_http()中經過Linux文件系統查找index.html文件並返回給瀏覽器:
if (opts.index_files == NULL) { opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php"; }
將mongoose.h、mongoose.c和simplest_web_server.c拷貝到本身的程序目錄下,同時將index.html也放置到該目錄下。
在Linux的Shell命令行輸入make命令,make工具會自動尋找當前目錄下的「makefile」文件進行解析,並生成最終的可執行文件.all,運行.all可啓動web服務器程序。
在服務器本機的瀏覽器中輸入http://localhost:1200/來訪問web服務器,以下圖所示:
經過另外一臺電腦的瀏覽器輸入http://192.168.20.1:1200/,以下圖所示: