Linux平臺上部署Mongoose服務器的方法介紹

前言

Mongoose是一個開源web服務器程序,僅有mongoose.c和 mongoose.h兩個文件,雖然小巧,但具有基本的web服務器的功能,同時Mongoose是用C編寫的,所以很是適合應用到嵌入式設備中。
目前Mongoose能夠支持嵌入到C/C++、Python、C#。本文主要介紹下如何在Linux環境下將Mongoose代碼嵌入到C應用程序中,並完成服務器的部署工做。
爲了更快地熟悉web服務器的特性,在介紹部署服務器以前,有必要先介紹下服務器和客戶端之間網絡通信的基礎傳輸技術。php


TCP鏈接過程

瀏覽器與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(num)和ACK(flag)值,並向服務器給出確認
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編程,Socket將TCP/IP協議封裝成API接口,
使得程序員更方便地使用TCP/IP協議棧。
Socket是以"打開—讀/寫—關閉"模式實現,以TCP協議通信的Socket爲例,交互流程大體以下,這裏從服務器的角度對流程進行介紹:編程

圖片描述

  • 建立socket()。socket()根據指定的協議族(IPv4/IPv6)、通訊類型(TCP/UDP)在sockfs文件系統中分配一個使用socket描述字關聯的資源,進程能夠像訪問一個已經打開的文件同樣訪問socket資源。
  • 綁定bind()。bind()在這個socket上綁定一個指定的端口號和IP地址。網絡通訊歸根結底可視爲不一樣計算機上的進程間通信,IP地址只能肯定進程所在的計算機,而結合端口號能夠惟一肯定整個網絡中的一個網絡進程。當IP地址爲INADDR_ANY(0)時,表示本地計算機的默認IP地址。
  • 監聽listen()。服務器socket端口一直處於等待狀態,監聽網絡中全部客戶端對端口號請求,隨時準備接收客戶端發來的鏈接
  • 接受accept()。服務器socket監聽到客戶端請求以後,將請求放在等待隊列中,accept()從等待隊列中提取鏈接請求,建立一個新的socket進行操做,而原來所監聽的socket不受影響。
  • 讀/寫write()/read()。發送內容即向socket寫入內容,讀取內容即從socket獲取內容
  • 關閉close()。close()關閉socket。

MakeFile

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

Linux平臺運行

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/,以下圖所示:

圖片描述

相關文章
相關標籤/搜索