在 acl 的協議庫(lib_protocol) 中有專門針對 HTTP 協議和 ICMP 協議的,本文主要介紹如何使用 lib_protocol 協議庫來開發一個簡單的 http 客戶端。下面首先介紹一下幾個本文用到的函數接口。html
/** * 建立一個 HTTP_UTIL 請求對象 * @param url {const char*} 完整的請求 url * @param method {const char*} 請求方法,有效的請求方法有:GET, POST, HEAD, CONNECT * @return {HTTP_UTIL*} */ HTTP_API HTTP_UTIL *http_util_req_new(const char *url, const char *method); /** * 設置 HTTP 代理服務器地址 * @param http_util {HTTP_UTIL*} * @param proxy {const char*} 代理服務器地址,有效格式爲: IP:PORT, DOMAIN:PORT, * 如: 192.168.0.1:80, 192.168.0.2:8088, www.g.cn:80 */ HTTP_API void http_util_set_req_proxy(HTTP_UTIL *http_util, const char *proxy); /** * 設置 HTTP 響應體的轉儲文件,設置後 HTTP 響應體數據便會轉儲於該文件 * @param http_util {HTTP_UTIL*} * @param filename {const char*} 轉儲文件名 * @return {int} 若是返回值 < 0 則表示沒法打開該文件, 不然表示打開文件成功 */ HTTP_API int http_util_set_dump_file(HTTP_UTIL *http_util, const char *filename); /** * 打開遠程 HTTP 服務器或代理服務器鏈接,同時構建 HTTP 請求頭數據而且將該數據 * 發給新創建的網絡鏈接 * @param http_util {HTTP_UTIL*} * @return {int} 0: 成功; -1: 沒法打開鏈接或發送請求頭數據失敗 */ HTTP_API int http_util_req_open(HTTP_UTIL *http_util); /** * 發送完請求數據後調用此函數從 HTTP 服務器讀取完整的 HTTP 響應頭 * @param http_util {HTTP_UTIL*} * @return {int} 0: 成功; -1: 失敗 */ HTTP_API int http_util_get_res_hdr(HTTP_UTIL *http_util); /** * 讀完 HTTP 響應頭後調用此函數從 HTTP 服務器讀取 HTTP 數據體數據,須要連續調用 * 此函數,直至返回值 <= 0, 若是以前設置了轉儲文件或轉儲則在讀取數據過程當中同時會 * 拷貝一份數據給轉儲文件或轉儲流 * @param http_util {HTTP_UTIL*} * @param buf {char *} 存儲 HTTP 響應體的緩衝區 * @param size {size_t} buf 的空間大小 * @return {int} <= 0: 表示讀結束; > 0: 表示本次讀到的數據長度 */ HTTP_API int http_util_get_res_body(HTTP_UTIL *http_util, char *buf, size_t size);
以上僅是 lib_http_util.h 函數接口中的一部分,下面就寫一個簡單的例子:服務器
#include "lib_acl.h" #include "lib_protocol.h" static void get_url(const char *method, const char *url, const char *proxy, const char *dump, int out) { /* 建立 HTTP_UTIL 請求對象 */ HTTP_UTIL *http = http_util_req_new(url, method); int ret; /* 若是設定代理服務器,則鏈接代理服務器地址, * 不然使用 HTTP 請求頭裏指定的地址 */ if (proxy && *proxy) http_util_set_req_proxy(http, proxy); /* 設置轉儲文件 */ if (dump && *dump) http_util_set_dump_file(http, dump); /* 輸出 HTTP 請求頭內容 */ http_hdr_print(&http->hdr_req->hdr, "---request hdr---"); /* 鏈接遠程 http 服務器 */ if (http_util_req_open(http) < 0) { printf("open connection(%s) error\n", http->server_addr); http_util_free(http); return; } /* 讀取 HTTP 服務器響應頭*/ ret = http_util_get_res_hdr(http); if (ret < 0) { printf("get reply http header error\n"); http_util_free(http); return; } /* 輸出 HTTP 響應頭 */ http_hdr_print(&http->hdr_res->hdr, "--- reply http header ---"); /* 若是有數據體則開始讀取 HTTP 響應數據體部分 */ while (1) { char buf[4096]; ret = http_util_get_res_body(http, buf, sizeof(buf) - 1); if (ret <= 0) break; buf[ret] = 0; if (out) printf("%s", buf); } http_util_free(http); } static void usage(const char *procname) { printf("usage: %s -h[help] -t method -r url -f dump_file -o[output] -X proxy_addr\n" "example: %s -t GET -r http://www.sina.com.cn/ -f url_dump.txt\n", procname, procname); } int main(int argc, char *argv[]) { int ch, out = 0; char url[256], dump[256], proxy[256], method[32]; acl_init(); /* 初始化 acl 庫 */ ACL_SAFE_STRNCPY(method, "GET", sizeof(method)); url[0] = 0; dump[0] = 0; proxy[0] = 0; while ((ch = getopt(argc, argv, "hor:t:f:X:")) > 0) { switch (ch) { case 'h': usage(argv[0]); return (0); case 'o': out = 1; break; case 'r': ACL_SAFE_STRNCPY(url, optarg, sizeof(url)); break; case 't': ACL_SAFE_STRNCPY(method, optarg, sizeof(method)); break; case 'f': ACL_SAFE_STRNCPY(dump, optarg, sizeof(dump)); break; case 'X': ACL_SAFE_STRNCPY(proxy, optarg, sizeof(proxy)); break; default: break; } } if (url[0] == 0) { usage(argv[0]); return (0); } get_url(method, url, proxy, dump, out); return (0); }
編譯成功後,運行 ./url_get -h 會給出以下提示:網絡
usage: ./url_get -h[help] -t method -r url -f dump_file -o[output] -X proxy_addr
example: ./url_get -t GET -r http://www.sina.com.cn/ -f url_dump.txt函數
輸入: ./url_get -t GET -r http://www.sina.com -o, 該命令是獲取 www.sina.com 頁面並輸出至標準輸出,獲得的結果爲:url
HTTP/1.0 301 Moved Permanently Date: Tue, 12 Jan 2010 01:54:39 GMT Server: Apache Location: http://www.sina.com.cn/ Cache-Control: max-age=3600 Expires: Tue, 12 Jan 2010 02:54:39 GMT Vary: Accept-Encoding Content-Length: 231 Content-Type: text/html; charset=iso-8859-1 Age: 265 X-Cache: HIT from tj175-135.sina.com.cn Connection: close --------------- end ----------------- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>301 Moved Permanently</title> </head><body> <h1>Moved Permanently</h1> <p>The document has moved <a href="http://www.sina.com.cn/">here</a>.</p> </body></html>
若是想把頁面轉存至文件中,能夠輸入:./url_get -t GET -r http://www.sina.com -f dump.txt, 這樣就會把新浪的首頁下載並存儲於 dump.txt 文件中。spa
這個例子很是簡單,其實若是查看 http_util.c 源碼,會看到這個文件是對 lib_http.h 裏一些更爲底層 API 的封裝。.net
若是僅是下載一個頁面至某個文件中,其實還有更爲簡單的方法,只須要調用接口:代理
/** * 將某個 url 的響應體數據轉儲至某個文件中 * @param url {const char*} 完整請求 url, 如: http://www.g.cn * @param dump {const char*} 轉儲文件名 * @param {int} 讀到的響應體數據長度, >=0: 表示成功, -1: 表示失敗 */ HTTP_API int http_util_dump_url(const char *url, const char *dump);
只這一個函數即可以達到與上一個例子相同的效果。code
能夠從 acl.sourceforge.net 上下載最新的 acl_project 庫,查看 samples/http/ 下的三個例子,看一下下載WEB文件的不一樣方式。server
acl bbs:http://www.aclfans.com/