在" 使用 acl 庫開發一個 HTTP 下載客戶端" 文章中介紹利用ACL庫中的 HTTP 高級API函數編寫HTTP下載客戶端的簡單的例子,本文介紹一下如何使用稍微底層的API來編寫一樣功能的例子。在這個例子中,能夠看到那些高級API是如何封裝底層API的。服務器
請先看一個例子以下:網絡
#include "lib_acl.h" #include "lib_protocol.h" static void get_url(const char *method, const char *url, const char *proxy, const char *dump) { /* 建立 HTTP 請求頭 */ HTTP_HDR_REQ *hdr_req = http_hdr_req_create(url, method, "HTTP/1.1"); ACL_VSTREAM *stream; /* 網絡鏈接流 */ ACL_VSTRING *buf = acl_vstring_alloc(256); /* 分配內存緩衝區 */ HTTP_HDR_RES *hdr_res; /* HTTP 響應頭 */ HTTP_RES *res; /* HTTP響應體 */ ACL_FILE *fp = NULL; /* 轉儲文件句柄 */ const char *ptr; int ret; /* 輸出 HTTP 請求頭內容 */ http_hdr_print(&hdr_req->hdr, "---request hdr---"); /* 若是設定代理服務器,則鏈接代理服務器地址, * 不然使用 HTTP 請求頭裏指定的地址 */ if (*proxy) acl_vstring_strcpy(buf, proxy); else acl_vstring_strcpy(buf, http_hdr_req_host(hdr_req)); /* 得到遠程 HTTP 服務器的鏈接地址 */ ptr = acl_vstring_memchr(buf, ':'); if (ptr == NULL) acl_vstring_strcat(buf, ":80"); else { int port; ptr++; port = atoi(ptr); if (port <= 0 || port >= 65535) { printf("http server's addr(%s) invalid\n", acl_vstring_str(buf)); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } } /* 鏈接遠程 http 服務器 */ stream = acl_vstream_connect(acl_vstring_str(buf) /* 服務器地址 */, ACL_BLOCKING /* 採用阻塞方式 */, 10 /* 鏈接超時時間爲 10 秒 */, 10 /* 網絡 IO 操做超時時間爲 10 秒 */, 4096 /* stream 流緩衝區大小爲 4096 字節 */); if (stream == NULL) { /* 鏈接服務器失敗 */ printf("connect addr(%s) error(%s)\n", acl_vstring_str(buf), acl_last_serror()); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } /* 構建 HTTP 請求頭數據 */ http_hdr_build_request(hdr_req, buf); /* 向 HTTP 服務器發送請求 */ ret = acl_vstream_writen(stream, acl_vstring_str(buf), ACL_VSTRING_LEN(buf)); if (ret == ACL_VSTREAM_EOF) { printf("write to server error(%s)\n", acl_last_serror()); acl_vstream_close(stream); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } /* 建立一個 HTTP 響應頭對象 */ hdr_res = http_hdr_res_new(); /* 讀取 HTTP 服務器響應頭*/ ret = http_hdr_res_get_sync(hdr_res, stream, 10 /* IO 超時時間爲 10 秒 */); if (ret < 0) { printf("get http reply header error(%s)\n", acl_last_serror()); http_hdr_res_free(hdr_res); acl_vstream_close(stream); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } /* 分析HTTP服務器響應頭 */ if (http_hdr_res_parse(hdr_res) < 0) { printf("parse http reply header error\n"); http_hdr_print(&hdr_res->hdr, "--- reply http header ---"); http_hdr_res_free(hdr_res); acl_vstream_close(stream); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } /* 若是須要轉儲至磁盤則須要先打開文件 */ if (dump != NULL) { fp = acl_fopen(dump, "w+"); if (fp == NULL) printf("open file(%s) error(%s)\n", dump, acl_last_serror()); } /* 若是 HTTP 響應沒有數據體則僅輸出 HTTP 響應頭便可 */ if (hdr_res->hdr.content_length == 0 || (hdr_res->hdr.content_length == -1 && !hdr_res->hdr.chunked && hdr_res->reply_status > 300 && hdr_res->reply_status < 400)) { if (fp) http_hdr_fprint(ACL_FSTREAM(fp), &hdr_res->hdr, "--- reply http header ---"); else http_hdr_fprint(ACL_VSTREAM_OUT, &hdr_res->hdr, "--- reply http header ---"); http_hdr_res_free(hdr_res); acl_vstream_close(stream); acl_vstring_free(buf); http_hdr_req_free(hdr_req); return; } /* 輸出 HTTP 響應頭 */ http_hdr_print(&hdr_res->hdr, "--- reply http header ---"); /* 建立 HTTP 響應體對象 */ res = http_res_new(hdr_res); /* 若是有數據體則開始讀取 HTTP 響應數據體部分 */ while (1) { http_off_t n; char buf2[4096]; /* 以同步方式讀取HTTP響應數據 */ n = http_res_body_get_sync(res, stream, buf2, sizeof(buf2) - 1); if (n <= 0) break; if (fp) { /* 轉儲至文件中 */ if (acl_fwrite(buf2, (size_t) n, 1, fp) == (size_t) EOF) { printf("write to dump file(%s) error(%s)\n", dump, acl_last_serror()); break; } } else { buf2[n] = 0; printf("%s", buf2); } } if (fp) acl_fclose(fp); /* 關閉轉儲文件句柄 */ http_res_free(res); /* 釋放 HTTP 響應對象, hdr_res 會在此函數內部自動被釋放 */ acl_vstream_close(stream); /* 關閉網絡流 */ acl_vstring_free(buf); /* 釋放內存區 */ http_hdr_req_free(hdr_req); /* 釋放 HTTP 請求頭對象 */ } static void usage(const char *procname) { printf("usage: %s -h[help] -t method -r url -f dump_file -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; 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, "hr:t:f:X:")) > 0) { switch (ch) { case 'h': usage(argv[0]); return (0); 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); return (0); }
能夠明顯地看出,該例子的實現代碼量要比 使用 acl 庫開發一個 HTTP 下載客戶端 麻煩許多,但它卻比較清晰地展現了 HTTP 協議的請求與響應過程。該例子能夠在 acl_project/samples/http/get_url1/ 目錄下找到。函數
acl 庫下載: http://acl.sourceforge.net/ui
我的微博:http://weibo.com/zsxxszurl