我自己在實現一個http client,使用libevent,遇到一些問題,串連可以建立,但發送http請求後毫無反應。實驗了windows和linux兩個版本,都是如此。可能還是我使用上的問題。
根據網上的例子修改了一下,可以在windows下面跑,沒問題了。代碼如下:
#include <stdlib.h>#include <string.h>#include "event2/event.h"#include "event2/http.h"#include "event2/buffer.h"#include "event2/http_struct.h"#include "event2/dns.h"struct download_context{ struct evhttp_uri * uri; struct event_base * base; struct evdns_base * dnsbase; struct evhttp_connection * conn; struct evhttp_request *req; struct evbuffer *buffer; int ok;};static void download_callback(struct evhttp_request *req, void *arg);static int download_renew_request(struct download_context *ctx);static void download_callback(struct evhttp_request *req, void *arg){ struct download_context * ctx = (struct download_context*)arg; struct evhttp_uri * new_uri = 0; const char * new_location = 0; if(!req){ printf("timeout\n"); return; } switch(req->response_code) { case HTTP_OK: event_base_loopexit(ctx->base, 0); break; case HTTP_MOVEPERM: case HTTP_MOVETEMP: new_location = evhttp_find_header(req->input_headers, "Location"); if(!new_location) return; new_uri = evhttp_uri_parse(new_location); if(!new_uri)return; evhttp_uri_free(ctx->uri); ctx->uri = new_uri; download_renew_request(ctx); return; default:/* failed */ event_base_loopexit(ctx->base, 0); return; } evbuffer_add_buffer(ctx->buffer, req->input_buffer); ctx->ok = 1;}struct download_context * context_new(const char *url){ struct download_context * ctx = 0; ctx = (struct download_context*)calloc(1, sizeof(struct download_context)); ctx->uri = evhttp_uri_parse(url); if(!ctx->uri) return 0; ctx->base = event_base_new(); ctx->buffer = evbuffer_new(); ctx->dnsbase = evdns_base_new(ctx->base, 1); download_renew_request(ctx); return ctx;}void context_free(struct download_context *ctx){ if(ctx->conn) evhttp_connection_free(ctx->conn); if(ctx->buffer) evbuffer_free(ctx->buffer); if(ctx->uri) evhttp_uri_free(ctx->uri); free(ctx);}static int download_renew_request(struct download_context *ctx){ int port = evhttp_uri_get_port(ctx->uri); if(port == -1) port = 80; if(ctx->conn) evhttp_connection_free(ctx->conn); printf("host:%s, port:%d, path:%s\n", evhttp_uri_get_host(ctx->uri), port, evhttp_uri_get_path(ctx->uri)); ctx->conn = evhttp_connection_base_new(ctx->base, ctx->dnsbase, evhttp_uri_get_host(ctx->uri), port ); ctx->req = evhttp_request_new(download_callback, ctx); evhttp_make_request(ctx->conn, ctx->req, EVHTTP_REQ_GET, evhttp_uri_get_path(ctx->uri)); evhttp_add_header(ctx->req->output_headers, "Host", evhttp_uri_get_host(ctx->uri)); return 0;}struct evbuffer *download_url(const char *url){ struct download_context * ctx = context_new(url); if(!ctx) return 0; event_base_dispatch(ctx->base); struct evbuffer * retval = 0; if(ctx->ok) { retval = ctx->buffer; ctx->buffer = 0; } context_free(ctx); return retval;}int main(int argc, char **argv){ struct evbuffer * data = 0; if(argc < 2){ printf("usage: %s example.com/\n", argv[0]); return 1; }#ifdef WIN32WORD wVersionRequested;WSADATA wsaData;wVersionRequested = MAKEWORD(2, 2);(void) WSAStartup(wVersionRequested, &wsaData);#endif data = download_url(argv[1]); printf("got %d bytes\n", data ? evbuffer_get_length(data) : -1); if(data) { const unsigned char * joined = evbuffer_pullup(data, -1); printf("data itself:\n====================\n"); fwrite(joined, evbuffer_get_length(data), 1, stderr); printf("\n====================\n"); evbuffer_free(data); } return 0;}
需要注意的是,如果從命令列傳入網址,比如百度的,必須傳入http://www.baidu.com/,否則解析不出來path,下載會失敗。
這個例子不是我最終想要的效果,無法控制下載和資料處理過程,它是等下載完成了一次性返回資料。對於大檔案,比如幾百兆甚至上G的,這種處理策略還不夠。我寫了一個http處理的模組和一些輔助代碼,編譯通過,可以運行,但請求發出後對端始終無反應。等我查一查,網上始終找不到libevent http client的例子,有echoserver之類的,就是沒有用戶端。
研究研究吧,可能是事件的使用上有問題。