使用 acl 庫開發一個 HTTP 下載客戶端

  在 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

 

我的微博:http://weibo.com/zsxxsz

acl bbs:http://www.aclfans.com/

相關文章
相關標籤/搜索