libevent实现客户端

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "event.lib")
#pragma comment(lib, "event_extra.lib")
#pragma comment(lib, "event_core.lib")
#else
#include <netinet/in.h>
#include <pthread.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#define GetCurrentThreadId() pthread_self()
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/thread.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9638;

static struct event_base * createEventBase();

static void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void timer_callback(evutil_socket_t, short, void *);

static void connect_server(struct event_base* base);

int mainC(int argc, char **argv)
{
    struct event_base *base;
    struct event* timer = NULL;
#ifdef WIN32
    WSADATA wsa_data;
    WSAStartup(0x0201, &wsa_data);
#endif

    base = createEventBase();

    if (!base)
    {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    connect_server(base);
    
    //构建并添加一个定时器,它的另一个作用就是让这个iocp模型一直保持着运行直到调用了event_base_loopexit
    //通过调试发现,当客户端因为某种原因断开了,那么这个iocp客户端模型会自动结束的,因为base中并没有事件,所以会退出。
    //为了让这个iocp一直正常的运行下去,我们可以添加一个永久定时器。
    timer = event_new(base, -1, EV_TIMEOUT | EV_PERSIST, timer_callback, NULL);
    {
        struct timeval timeout = {1,0};
        event_add(timer, &timeout);
        //event_add(timer, NULL);
    }

    event_base_dispatch(base); //这里面进入循环了

    event_free(timer);

    event_base_free(base);

#ifdef WIN32
    WSACleanup();
#endif
    printf("done\n");
    return 0;
}

/**************************************************************************************************************************
libevent的一些问题记录。
1. 读写时,可能存在最大包字节限制,因为默认为16384字节,下面这2个宏是内部定义的。
#define MAX_SINGLE_READ_DEFAULT 16384
#define MAX_SINGLE_WRITE_DEFAULT 16384
可以通过bufferevent_set_max_single_read(...),bufferevent_set_max_single_write(...)去设置最大值。
*/
static struct event_base * createEventBase()
{
    struct event_base *base = NULL;
#if 1
    base = event_base_new();
#else
    struct event_config *cfg = event_config_new();
#ifdef WIN32
    //告诉libEvent使用Windows自己的线程和同步锁,并启用多线程安全
    evthread_use_windows_threads();
    //Windows启用IOCP模式
    event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
    //根据CPU实际数量配置libEvent的CPU数
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    event_config_set_num_cpus_hint(cfg, 2);//si.dwNumberOfProcessors
#else
    //告诉libEvent使用平台自己的线程和同步锁,并启用多线程安全
    evthread_use_pthreads();
#endif
    base = event_base_new_with_config(cfg);
    event_config_free(cfg);
    cfg = NULL;
#endif
    return base;
}

static void timer_callback(evutil_socket_t fd, short event, void *user_data)
{
    struct timeval tv;
    evutil_gettimeofday(&tv, nullptr);
    printf("it's timeout....%d,%d\n", tv.tv_sec, tv.tv_usec);
}

/*
如果要多个客户端,只需要多次调用这个,也可以创建多个event_base
*/
static void connect_server(struct event_base* base)
{
    //struct sockaddr_in6 sin6; ipv6
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
    evutil_inet_pton(sin.sin_family, "127.0.0.1", (void *)&sin.sin_addr);

    /*如果想要读写和释放在别的线程中也能够安全操作,则需要,添加 BEV_OPT_THREADSAFE,同时需要在最开始的时候调用
        evthread_use_windows_threads()、evthread_use_pthreads()告诉平台启用多线程*/
    struct bufferevent *bev;
#if 1
    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
#else
    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
#endif
    if (!bev)
    {
        return;
    }
    //设置读写和客户端连接状态的回调函数
    bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, 0);

    //设置读写回调是否可用
    bufferevent_enable(bev, EV_WRITE | EV_READ);

    if (0 != bufferevent_socket_connect(bev, (struct sockaddr *)&sin, sizeof(sin)))
    {
        fprintf(stderr, "Could not bufferevent_socket_connect!\n");
    }
}

//向客户端写完数据后发生的一个回调,通常是调用bufferevent_write后发生了回调。
static void conn_writecb(struct bufferevent *bev, void *user_data)
{
    struct evbuffer *output = bufferevent_get_output(bev);
    if (evbuffer_get_length(output) == 0)
    {
        printf("flushed answer\n");
    }
}

//当收到客户端发送过来的数据时,该函数发生了回调
static void conn_readcb(struct bufferevent *bev, void *user_data)
{
    struct evbuffer *input = bufferevent_get_input(bev);
    size_t len = evbuffer_get_length(input);
    if (!len)
    {
        puts("接收到的数据个数是0");
        return;
    }
    char data[1025] = "";
    size_t size = 0;
    //从缓冲区中读取接收到的数据
    while (0 != (size = bufferevent_read(bev, data, 1024)))
    {
        printf("data=%s, len=%d\n", data, size);
    }
    const char *wData = "send to client!";
    bufferevent_write(bev, wData, strlen(wData) + 1);

    //主动断开与客户端的连接
    //bufferevent_free(bev);
    printf("a new conn_readcb %p\n", bev);
}

static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
    printf("tcb-bev=%p\n", bev);
    if (events & BEV_EVENT_EOF)
    {
        printf("Connection closed.\n");
    }
    else if (events & BEV_EVENT_ERROR)
    {
        printf("Got an error on the connection: %s\n", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
    }
    else if (events & BEV_EVENT_TIMEOUT)
    {
        printf("timeout\n");
    }
    else if (events & BEV_EVENT_CONNECTED)
    {
        //connected success
        printf("connected socket=%d\n", bufferevent_getfd(bev));
        return;
    }

    // None of the other events can happen here, since we haven't enabled timeouts
    bufferevent_free(bev);
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个基于libevent实现的WebSocket客户端的示例代码: ``` #include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h> #include <event2/http_struct.h> #include <event2/keyvalq_struct.h> #include <event2/bufferevent_ssl.h> #include <openssl/ssl.h> #include <openssl/err.h> static void websocket_readcb(struct bufferevent *bev, void *ctx); static void websocket_eventcb(struct bufferevent *bev, short events, void *ctx); int main(int argc, char **argv) { struct event_base *base; struct bufferevent *bev; struct sockaddr_in sin; SSL_CTX *ssl_ctx; SSL *ssl; base = event_base_new(); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_port = htons(8080); bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, websocket_readcb, NULL, websocket_eventcb, NULL); bufferevent_enable(bev, EV_READ | EV_WRITE); bufferevent_socket_connect(bev, (struct sockaddr *)&sin, sizeof(sin)); ssl_ctx = SSL_CTX_new(TLSv1_2_client_method()); ssl = SSL_new(ssl_ctx); bufferevent_openssl_set_ssl(bev, ssl, BUFFEREVENT_SSL_CONNECTING); bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); SSL_set_tlsext_host_name(ssl, "localhost"); // 发送WebSocket握手请求 char request[1024]; sprintf(request, "GET /websocket HTTP/1.1\r\n" "Host: localhost:8080\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n"); bufferevent_write(bev, request, strlen(request)); event_base_dispatch(base); SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ssl_ctx); bufferevent_free(bev); event_base_free(base); return 0; } static void websocket_readcb(struct bufferevent *bev, void *ctx) { struct evbuffer *input = bufferevent_get_input(bev); char *line = NULL; size_t len = 0; ssize_t nread; while ((nread = evbuffer_get_line(input, &line, &len, EVBUFFER_EOL_CRLF)) > 0) { printf("%s\n", line); free(line); } } static void websocket_eventcb(struct bufferevent *bev, short events, void *ctx) { if (events & BEV_EVENT_CONNECTED) { printf("Connected.\n"); } else if (events & BEV_EVENT_ERROR) { printf("Error.\n"); } else if (events & BEV_EVENT_EOF) { printf("Connection closed.\n"); event_base_loopbreak(bufferevent_get_base(bev)); } } ``` 这个示例代码中,首先创建了一个event_base对象,然后创建了一个bufferevent对象,并设置了回调函数。接着,使用bufferevent_socket_connect函数连接到WebSocket服务器,然后创建了一个SSL_CTX对象和一个SSL对象,并使用bufferevent_openssl_set_ssl函数将SSL对象关联到bufferevent对象上。接下来,发送了一个WebSocket握手请求。最后,调用event_base_dispatch函数进入事件循环。 在事件循环中,如果接收到了数据,就调用websocket_readcb函数进行处理。如果连接建立成功,就输出Connected。如果发生错误,就输出Error。如果连接关闭,就输出Connection closed,并调用event_base_loopbreak函数退出事件循环。 需要注意的是,这个示例代码中使用了OpenSSL库,所以需要链接libssl和libcrypto库。在Linux系统中,可以使用以下命令编译: ``` $ gcc -o websocket-client websocket-client.c -levent -lssl -lcrypto ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值