`
king_tt
  • 浏览: 2285308 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

libevent实现http client

 
阅读更多

使用libevent实现了一个http client。

请给我的决赛文章Qt Quick 图像处理实例之美图秀秀(附源码下载)投票,谢谢。

一直想找一个基于libevent实现的client端的例子,没找着合适的,自己做了一个。遇到一个问题,发出http请求后,对方总是无反应。今天研究evhttp源码,忽然发现了一个愚蠢的错误:发送请求时少了一个空行("\r\n")。啊,太有才了耶。

其实是想利用libevent的事件处理机制,想在linux和安卓两个系统上共享一些功能模块,先拿httpclient做个试验。用到了http_parser来解析httpheader,特此感谢。

是在纯C的环境下试验,我实现了两个辅助结构体(通过函数指针模仿类的实现)。struct c_string用于字符串处理,struct tag_value_list用来存储http header field和header value。我的试验是熟悉libevent,这些代码掠过。

来看一下main.c的代码,主要是如何使用libevent。

#include "event2/event-config.h"
#include "event2/event_compat.h"
#include "event2/event.h"
#include "event2/util.h"
#include "event2/bufferevent.h"
#include "event2/dns.h"
#include "event2/buffer.h"
#include "http_client.h"
#ifndef WIN32
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <string.h>

void write_cb(evutil_socket_t sock, short flags, void * args)
{
    struct http_client *httpc = (struct http_client *)args; 
    struct c_string * string = httpc->request_string(httpc);
    int len = string->len(string);
    int sent = 0;
    int ret = 0;

    printf("connected, write headers: %s\n", string->data);

    ret = send(sock, string->data, len, 0);
    while(ret != -1)
    {
        sent += ret;
        if(sent == len) break;
        ret = send(sock, string->data + sent, len - sent, 0);
    }

    delete_c_string(string);

    event_add((struct event*)httpc->user_data[1], 0);
}

void read_cb(evutil_socket_t sock, short flags, void * args)
{
    struct http_client *httpc = (struct http_client*)args;
    int ret = recv(sock, httpc->parse_buffer, PARSE_BUFFER_SIZE, 0);
    
    printf("read_cb, read %d bytes\n", ret);
    if(ret > 0)
    {
        httpc->process_data(httpc, httpc->parse_buffer, ret);
    }
    else if(ret == 0)
    {
        printf("read_cb connection closed\n");
        event_base_loopexit((struct event_base*)httpc->user_data[0], NULL);
        return;
    }
    if(httpc->finished(httpc) != 0)
    {
        event_add((struct event*)httpc->user_data[1], 0);
    }
}

static evutil_socket_t make_tcp_socket()
{
    int on = 1;
    evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    evutil_make_socket_nonblocking(sock);
#ifdef WIN32
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on, sizeof(on));
#else
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
#endif

    return sock;
}

static struct http_client * make_http_client(struct event_base * base, const char *url)
{
    struct http_client * httpc = new_http_client();
    /* initialize http client */
    httpc->user_data[0] = base;
    if(0 == httpc->parse_url(httpc, url) )
    {
        httpc->add_request_header(httpc, "Accept", "*/*");
        httpc->add_request_header(httpc, "User-Agent", "test http client");
        return httpc;
    }

    delete_http_client(httpc);
    printf("parse url failed\n");
    return 0;
}

int download(struct event_base * base, const char *url)
{
    evutil_socket_t sock = make_tcp_socket();
    struct sockaddr_in serverAddr;
    struct http_client * httpc = make_http_client(base, url);
    struct event * ev_write = 0;
    struct event * ev_read = 0;
    struct timeval tv={10, 0};

    if(!httpc) return -1;

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(httpc->port);
#ifdef WIN32
    serverAddr.sin_addr.S_un.S_addr = inet_addr(httpc->host);
#else
    serverAddr.sin_addr.s_addr = inet_addr(httpc->host);
#endif
    memset(serverAddr.sin_zero, 0x00, 8);

    connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr));

    ev_write = event_new(base, sock, EV_WRITE, write_cb, (void*)httpc);
    ev_read = event_new(base, sock, EV_READ , read_cb, (void*)httpc);
    httpc->user_data[1] = ev_read;
    event_add(ev_write, &tv);

    return 0;
}


int main(int argc, char** argv)
{
    struct event_base * base = 0;
#ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;

    wVersionRequested = MAKEWORD(2, 2);

    (void) WSAStartup(wVersionRequested, &wsaData);
#endif

    if(argc < 2)
    {
        printf("usage: %s http://111.222.333.44:8080/xxx.htm\n    now only support ip.\n", argv[0]);
        return 1;
    }

    base = event_base_new();

    if( 0 == download(base, argv[1]) )
    {
        event_base_dispatch(base);
        event_base_free(base);
    }
    else
    {
        printf("prepare download failed for %s\n", argv[1]);
    }

    return 0;
}

例子比较简单,通过命令行传参,下载指定的URL代表的资源,下载到的资源也没有处理(后续可以通过给struct http_client添加数据处理的回调接口来处理数据)。目前可以在windows和cent os上运行。

程序不严谨,一些错误未处理,一些内存未释放。仅仅演示如何使用libevent实现client程序。大概经历下列步骤即可:

  1. 初始化event_base(后续要运行事件循环)
  2. 创建socket,设置为异步,连接server
  3. 创建写读写事件,先将写事件加入事件循环
  4. 在写事件回调中向server端发送请求并将读事件加入事件循环
  5. 在读事件回调中处理数据,并根据数据是否读取完毕决定是否继续添加读事件

至于协议的处理细节,根据程序目的可以自由实现。

我没有使用bufferevent,网上有很多的例子,不再这里提了。接下来我希望试验一下UDP,看能否成功。


分享到:
评论

相关推荐

    【最新】C++ 使用libEvent实现http的post, get功能

    本篇将深入探讨如何利用libEvent库在C++中实现HTTP的POST和GET请求。 首先,理解HTTP的基本概念至关重要。HTTP(超文本传输协议)是互联网上应用最广泛的一种网络协议,用于客户端和服务器之间的通信。GET和POST是...

    linux下使用libevent实现断网重连的tcp客户端

    linux下使用libevent实现断网重连的tcp客户端,自动检测tcp连接断开,断开后能自动重连;如果连不上服务器,则一直尝试连接服务器,直至连接成功。

    libevent服务器和客户端

    在这个项目中,我们实现了一个基于libevent的简单服务器和客户端,它们能够互相发送数据,这对于理解libevent的工作原理及其在网络编程中的应用至关重要。 ### 1. libevent简介 libevent的核心功能是提供一个抽象...

    libevent.rar

    在本压缩包“libevent.rar”中,包含了一个简化版的 Libevent HTTP 服务器和客户端示例代码,分别为 "libevent_server.cpp" 和 "libevent_client.cpp"。 首先,让我们详细了解一下 Libevent 的核心功能和工作原理。...

    libevent 2.1.12版本,编译好的全部文件,包含lib和头文件

    Libevent包含了简单的HTTP服务器框架,可以快速构建HTTP服务,处理HTTP请求和响应。 8. **性能优化** 在2.1.12版本中,libevent可能对内部数据结构和算法进行了优化,提升了事件处理速度,减少了内存占用。 9. *...

    一个libevent学习用的服务端客户端

    根据提供的压缩包文件名称"ServerLibevent",我们可以推测这个项目包含了一个使用libevent实现的服务端示例。可能的目录结构如下: - ServerLibevent - src - server.c:服务端代码,使用libevent处理网络连接和...

    libevent多线程

    9. **Client与Server**:压缩包中的"Client"和"Server"文件可能包含的是使用Libevent实现的多线程TCP客户端和服务器的示例代码。这些代码可以帮助开发者了解如何在实践中使用Libevent处理多线程网络通信。 通过理解...

    memcached安装包以及MemCachedClient

    libevent 是一个事件通知库,Memcached 可以依赖它实现异步非阻塞I/O。当在编译 Memcached 时,如果选择使用 libevent 模式,可以提高其性能和可扩展性。libevent 提供了一种方式来处理多个长时间运行的连接,而无需...

    websocket:libuv之上的C ++ 17中的跨平台WebSocket实现

    C ++中的WebSocket v13实现 用法 # include " websocket.hpp " ... using namespace nc ; loop = uv_default_loop(); websocket::WebSocketClient ws (loop); ws.on_connection = [](websocket::WebSocket* ws,...

    libevent网络库

    如果包含的是 `libevent 注释版`,那么这个版本的库应该有更详尽的注释,帮助开发者理解内部实现和接口。 总的来说,Libevent 是一个强大的工具,可以帮助开发者编写出高效率、低延迟的网络程序。通过使用事件驱动...

    sipclient_linuxc商用版本_sip协议_源码

    "sipclient"的源码正是用C语言编写的,这意味着我们可以期待一个高效且内存占用低的客户端实现。 在ARM架构的Linux设备上运行SIP客户端需要考虑以下几点: 1. **兼容性**:由于ARM处理器架构不同于常见的x86/x64...

    evpp:现代C ++网络库,用于在TCPUDPHTTP协议中开发高性能网络服务

    基于libevent的内置HTTP服务器的无阻塞多线程HTTP服务器 非阻塞HTTP客户端 无阻塞多线程UDP服务器 异步DNS解析 EventLoop / ThreadPool / Timer 经过良好测试的在生产中每天都经过单元测试和压力测试的良好测试。 ...

    PHP实现WebSocket

    PHPAsyncWebSocketClient使用了PHP的异步和事件驱动编程模型,这通常是通过libevent或ReactPHP等底层库实现的。异步编程允许非阻塞I/O操作,提高程序并发性能。在WebSocket客户端中,这意味着当等待服务器响应时,...

    fdfs安装包.zip

    通过以上步骤,我们可以完成Fdfs分布式存储系统的部署,利用libevent实现高效的事件处理,利用FastDFS存储和同步文件,再通过fastdfs-nginx-module在Nginx上实现文件的HTTP访问,形成一个完整的文件存储解决方案。...

    Java开发中的Memcache原理及实现

    - **获取方式**:访问项目网址 (http://github.com/gwhalin/Memcached-Java-Client/) 下载客户端。 **4. 其他辅助工具** - **Libevent**:Linux环境下Memcached使用的事件处理库,用于Socket的处理。如果系统未...

    fastdfs.zip

    Nginx作为反向代理服务器,可以将HTTP请求转发到FastDFS的Tracker服务器,实现HTTP访问文件的功能。安装Nginx后,需要配置Nginx的FastDFS模块,将FastDFS的URL映射到Nginx的location中。 总结来说,FastDFS的安装...

    Centos7上安装FastDFS并用-java7-实现上传下载文件

    vim tracker.conf # 修改base_path和http.server_port mkdir /home/fastdfs /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart ``` **5. 配置storage服务器** Storage服务器是FastDFS集群中的实际存储节点。...

    memcached学习总结

    # wget http://www.monkey.org/~provos/libevent-1.2.tar.gz ``` 2. 解压并进入libevent目录: ``` # tar zxvf libevent-1.2.tar.gz # cd libevent-1.2 ``` 3. 配置、编译并安装libevent: ``` # ./...

    PHP Socket 网络应用框架 beyod

    支持TCP、UDP、Unix、SSL, 内置HTTP/WebSocket/Async Redis/Async TCP Client, 并支持自定义数据包解析,从而实现任何应用层协议。 SSL/reuse_port/cluster dispatcher/工作进程平滑重启等特性, 单个进程中可实现多...

    树莓派下编译seafile文档

    libevhtp是基于libevent的HTTP服务器库,在Seafile文件服务器中有应用: 1. 克隆libevhtp仓库: ```bash git clone https://www.github.com/haiwen/libevhtp.git ``` 2. 进入目录并编译安装: ```bash cd ...

Global site tag (gtag.js) - Google Analytics