使用 acl 較爲底層的 HTTP 協議庫寫 HTTP 下載客戶端舉例

  在" 使用 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

相關文章
相關標籤/搜索