輕量級iOS/OSX服務器GCDWebServer

簡述


GCDWebServer是一個基于GCD的輕量級服務器框架,用于內嵌到OSX或者iOS系統的應用中提供HTTP1.1的服務。


實現目標


1.設計優雅,易于使用。僅僅包含4個核心類:server, connection, request and response

2.設計良好的API。頭文件注釋齊全,非常易于繼承和定制個性化需求。

3.事件驅動模型。基于GCD框架,實現最佳性能和并發。

4.不依賴任何第三方源碼。

5.符合新的BSD許可協議。


額外特性


1.針對http請求,支持完全異步處理

2.針對較大HTTP請求和響應流,采用內存最優化策略

3.支持解析使用"application/x-www-form-urlencoded" 或者 "multipart/form-data"編碼格式提交的html表單

4.支持對json格式的請求或響應進行解析和序列化

5.HTTP請求或響應采用分塊傳輸編碼

6.HTTP請求和響應采用gzip方式壓縮

7.對本地文件的請求支持多種HTTP類型

8.采用通用、簡單的密碼保護訪問認證機制

9.支持在app前臺、后臺或掛起時自動處理事務

10.完全支持ipv4和ipv6


拓展功能


1.文件上傳功能。提供通過瀏覽器實現文件上傳和下載的接口。GCDWebUploader(->GCDWebServer)

2.DAV文件系統服務。DAV不僅被看作HTTP的擴展,甚至被看作一種網絡文件系統。(GCDWebDAVServer->GCDWebServer)


不支持


1.長連接

2.https請求


系統要求


1.OS X 10.7 or later (x86_64)

2.iOS 5.0 or later (armv7, armv7s or arm64)

3.僅支持ARC


如何開始


下載最新源碼,將整個GCDWebServer目錄拷貝到自己的xcode項目目錄下,importGCDWebServer.h頭文件即可。如果需要使用GCDWebDAVServer或GCDWebUploader,同樣拷貝對應的目錄到自己的項目工程下。

或者你也可以通過cocoaPods方式引入:

在xcode項目的podfile文件中添加如下語句:

pod "GCDWebServer", "~> 3.0”

如果需要使用上傳功能,用下面的替換:

pod "GCDWebServer/WebUploader", "~> 3.0”

如果需要使用DAV功能,用下面的替換:

pod "GCDWebServer/WebDAV", "~> 3.0”


Hello World


由于GCDWebServer采用GCD代碼塊實現請求處理handler,代碼清晰、整潔,因此不需要子類化或者代理方式。

基于iOS應用的實現步驟:

1.Create server

2.添加請求處理句柄(Handle)

3.啟動服務器,監聽8080端口



異步HTTP響應生成


GCDWebServer 3.0開始,框架擁有異步處理HTTP請求的能力,形如,為服務添加不同的handles來異步生成GCDWebServerResponse響應。要想實現這個功能,添加handle實現時需要使用GCDWebServerAsyncProcessBlock替換GCDWebServerProcessBlock。

生成靜態網頁

GCDWebServer包含一個內建的handler,可以提供遞歸地目錄服務(也可以讓你控制如何設置緩存控制的頭部信息)。

#import "GCDWebServer.h"

int main(int argc, const char* argv[]) {

@autoreleasepool {

GCDWebServer* webServer = [[GCDWebServer alloc] init];

[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES];

[webServer runWithPort:8080];

}

return 0;

}


GCDWebServer實戰


以創建gcdwebserver類的一個實例開始。請注意,你可以有多個Web服務器運行在同一個應用程序中,只要他們監聽不同的端口即可。

然后,向服務器添加一個或多個Handler:每個Handler可以處理一個外部傳入的Web請求,同時提供/生成響應。Handles處理隊列是一個后進先出隊列,所以最新添加的handler會覆蓋最先添加的handler。

最后,在一個給定的端口上開啟服務。

理解GCDWebServer的架構


GCDWebServer架構由4個核心類組成:

1.GCDWebServer輔助管理監聽HTTP連接的套接字以及服務器使用的處理器列表。

2.GCDWebServerConnection繼承自GCDWebServer,負責處理每個HTTP連接。每個實例都保持連接直到連接關閉。你不能直接使用這個類,但它是暴露的,所以你可以子類化它用于重寫一些鉤子。

3.GCDWebServerRequest由GCDWebServerConnection的實例在拿到http信息頭后創建。它負責 把請求和處理HTTP信息體封裝起來。GCDWebServer自帶的幾個GCDWebServerRequest子類負責處理普通請求,比如保存請求信息在內存或在磁盤上的文件上。

4.GCDWebServerResponse由請求程序的handle創建,負責將響應的HTTP頭和body封裝起來。GCDWebServer自帶的幾個GCDWebServerResponse 子類負責處理響應,比如保存請求信息在內存或在磁盤上的文件上。

實現Handlers


GCDWebServer依靠handlers去處理Web請求并生成響應。handlers由GCD塊實現,使得你可以很容易實現它們。然而,他們會在GCD中執行任意線程,所以要特別注意線程安全和重入問題。

1.GCDWebServerMatchBlock被添加到GCDWebServer的實例中,當任意請求開始時將會被調用(例如收到http請求的頭部信息)。它負責為web請求傳遞基本信息,并決定是否繼續處理。如果是,它必須返回一個新的帶有請求信息的GCDWebServerRequest實例(見上文)。否則,它只返回nil。

2.GCDWebServerProcessBlock或GCDWebServerAsyncProcessBlock將在web請求完全接收完畢后調用,并負責傳遞由上一步生成的GCDWebServerRequest實例。它必須返回同步(如果使用GCDWebServerProcessBlock)或異步(如果使用GCDWebServerAsyncProcessBlock)的GCDWebServerResponse實例(見上文)或帶500HTTP狀態碼返回給客戶端的nil。當然,更推薦返回GCDWebServerErrorResponse實例,這樣一來可以更多有用的信息返回給客戶端。

注意,大多數添加handlers的GCDWebServer方法只期望GCDWebServerProcessBlock或GCDWebServerAsyncProcessBlock,因為他們已經提供了一個內置的GCDWebServerMatchBlock,例如帶有正則表達式匹配的URL路徑。


GCDWebServer的后臺模式處理


當在iOS應用中處理網絡操作時,你必須小心處理當iOS應用程序進入后臺的情況。通常情況,當應用程序在后臺時,你必須停止所有網絡服務器,當應用程序返回到前臺時再重新啟動。考慮到服務器可能有正在進行的連接時,他們需要停止,這種場景可能會變得相當復雜。

幸運的是,GCDWebServer已經自動為你處理了上述所有事情。

1.在第一個HTTP連接打開時,GCDWebServer將開啟一個后臺任務,當最后一個HTTP連接斷開時這個后臺任務將結束。這可以防止當iOS App進入后臺時掛起,掛起后app會立即殺死所有與客戶端直連的HTTP連接。

當應用程序進入后臺時,只要新的HTTP連接被啟動,這個后臺任務將繼續存在和并且iOS不會掛起應用程序(除非突然和意外的內存壓力下)。

如果當最后一個HTTP連接被關閉應用程序仍然在后臺,只要你調用stop方法,GCDWebServer將自己暫停并停止接收新的連接。(這種行為可以通過GCDWebServerOption_AutomaticallySuspendInBackground選項禁用)。

2.如果應用程序切換到后臺并且沒有HTTP連接是打開的,只要你調用stop方法,GCDWebServer將立即暫停和停止接收新的連接。(這種行為可以與GCDWebServerOption_AutomaticallySuspendInBackground選項禁用)。

3.如果應用程序切換回到前臺,而GCDWebServer已經暫停,只要你調用start方法,它會自動恢復,開始再次接收新的HTTP連接。

HTTP連接往往以包的形式開始向外并發,例如加載一個有多資源的網頁。這使得它很難準確地檢測出最后的HTTP連接已關閉:很有可能2個屬于同一個分包但連續的HTTP連接將被一個短暫的延遲而非重疊分離。如果客戶端剛好在兩個連接之間的延遲部分掛起,這將會使問題變得很糟糕。GCDWebServerOption_AutomaticallySuspendInBackground選項可以優雅地解決這個問題,通過在最后一個HTTP連接關閉后強迫GCDWebServer等待一個額外的延遲,防止一個新的連接在延遲期間被分離。

GCDWebServer中的日志


為實現調試和查看信息,每當發生什么時,GCDWebServer都會對當前的服務狀態進行日志記錄。此外,當使用debug模式而不是release模式構建GCDWebServer,它將記錄下更多的信息,也進行了大量的內部一致性檢查。為了實現這一行為,編譯GCDWebServer時指定預處理器常量 DEBUG =1。在Xcode的目標設置中,也可以通過增加DEBUG = 1到編譯設置GCC_PREPROCESSOR_DEFINITIONS中。最后,你還可以在運行時調用+[GCDWebServer setLogLevel:]方法實現對冗長的日志進行進行控制。

默認情況下,所有通過GCDWebServer記錄的日志都會被發送到其內置的日志中心,它只是輸出到stderr(假設一個終端類型設備已連接)。為了更好地融入你的應用程序的其余部分或由于記錄的信息量較大,你可能希望使用另一個日志記錄中心。

GCDWebServer自動支持XLFacility(GCDWebServer的作者實現,已開源)和CocoaLumberjack。如果他們是在同一個Xcode項目中, GCDWebServer自動使用它們而不是內置日志中心(更多細節見 GCDWebServerPrivate.h文件)。

當然,也支持自定義日志功能,更多信息見GCDWebServer.h。

應用實例1:實現HTTP請求重定向


下面是一個將’/’重定向到’/index.html’的例子,使用GCDWebServerResponse提供的非常好用的方法(這個方法將設置HTTP狀態并自動定位頭文件)

{code}

[self addHandlerForMethod:@"GET"

path:@"/"

requestClass:[GCDWebServerRequest class]

processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL]

permanent:NO];

}];


應用實例2:實現表單


實現一個HTTP的表單,你需要兩個handlers:

1.GET處理handler不需要HTTP請求中的body信息,因此采用 GCDWebServerRequest類。該handler程序將生產一個包含一個簡單的HTML表單響應。

2.POST處理handler需要HTTP請求中經過encode后的body信息中的表單值。幸運的是,GCDWebServer提供請求的類GCDWebServerURLEncodedFormRequest可自動解析這些信息。該處理程序只簡單地從用戶提交的表單中獲取返回值。

[webServer addHandlerForMethod:@"GET"

path:@"/"

requestClass:[GCDWebServerRequest class]

processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

NSString* html = @" \

\

\

Value: \

\

\

\

";

return [GCDWebServerDataResponse responseWithHTML:html];

}];

[webServer addHandlerForMethod:@"POST"

path:@"/"

requestClass:[GCDWebServerURLEncodedFormRequest class]

processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"];

NSString* html = [NSString stringWithFormat:@"%@", value];

return [GCDWebServerDataResponse responseWithHTML:html];

}];


應用實例3:實現動態網頁


GCDWebServer提供一個擴展的GCDWebServerDataResponse類,可以返回根據模板和一組變量(使用格式%變量%)生成的HTML內容。這是一個非常基礎的模板系統,作為一個起點引入,最終通過子類GCDWebServerResponse實現更高級的模板系統。

假設你有一個網站目錄在你的應用程序中,包含HTML模板文件及相應的CSS,腳本和圖片,那么,把它變成一個動態網站將會跟容易:

{code}

// Get the path to the website directory

NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil];

// Add a default handler to serve static files (i.e. anything other than HTML files)

[self addGETHandlerForBasePath:@"/" directoryPath:websitePath indexFilename:nil cacheAge:3600 allowRangeRequests:YES];

// Add an override handler for all requests to "*.html" URLs to do the special HTML templatization

[self addHandlerForMethod:@"GET"

pathRegex:@"/.*\.html"

requestClass:[GCDWebServerRequest class]

processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

NSDictionary* variables = [NSDictionary dictionaryWithObjectsAndKeys:@"value", @"variable", nil];

return [GCDWebServerDataResponse responseWithHTMLTemplate:[websitePath stringByAppendingPathComponent:request.path]

variables:variables];

}];

// Add an override handler to redirect "/" URL to "/index.html"

[self addHandlerForMethod:@"GET"

path:@"/"

requestClass:[GCDWebServerRequest class]

processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL]

permanent:NO];

];

{code}


雖然可嵌入app的輕量級服務器還有CocoaHTTPServer,簡單試用下就可以明顯發現,GCDWebServer更加適合。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,982評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,476評論 25 708
  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx?那么一定聽過它的“同行”Apache吧!Ngi...
    JokerW閱讀 32,803評論 24 1,002
  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,183評論 6 13
  • 老話說久病床前無孝子。只是年輕人體驗不到這句話的份量。 瓊瑤阿姨的愛人,2002年就重病臥床,直到靠鼻飼管為生,而...
    秋葉大叔閱讀 896評論 1 6