簡述
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更加適合。