在 C 编程中使用 libevent 编写非阻塞事件

Posted

技术标签:

【中文标题】在 C 编程中使用 libevent 编写非阻塞事件【英文标题】:write non-blocking event with with libevent in C programming 【发布时间】:2013-12-12 06:28:37 【问题描述】:

我是 libevent 和套接字编程的新手,这就是为什么我对 libevent 如何以异步和非阻塞方式工作存在疑问。 这是参考代码。 https://github.com/libevent/libevent/blob/master/sample/http-server.c

static void dump_request_cb(struct evhttp_request *req, void *arg)

    const char *cmdtype;
    struct evkeyvalq *headers;
    struct evkeyval *header;
    struct evbuffer *buf;

    printf("Request Start\n");
    sleep(30); // delay to read request to check non blocking event.

    switch (evhttp_request_get_command(req)) 
    case EVHTTP_REQ_GET: cmdtype = "GET"; break;
    case EVHTTP_REQ_POST: cmdtype = "POST"; break;
    case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
    case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
    case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
    case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
    case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
    case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
    case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
    default: cmdtype = "unknown"; break;
    

    printf("Received a %s request for %s\nHeaders:\n",
        cmdtype, evhttp_request_get_uri(req));

    headers = evhttp_request_get_input_headers(req);

    for (header = headers->tqh_first; header;
       header = header->next.tqe_next) 
            printf("  %s: %s\n", header->key, header->value);
    
    buf = evhttp_request_get_input_buffer(req);

    puts("Input data: <<<");

    while (evbuffer_get_length(buf)) 
            int n;
            char cbuf[128];
            n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
            if (n > 0)
                    (void) fwrite(cbuf, 1, n, stdout);
    
    puts(">>>");

    evhttp_send_reply(req, 200, "OK", NULL);

我在上述请求中创建了 30 秒的延迟。

当我从浏览器发送两个请求时。代码应该立即开始一次服务两个请求。但这并没有发生。真实情况是第二个请求在第一个请求完成后服务,延迟 30 秒。这意味着处理两个请求总共需要 60 秒。

那么谁能告诉我它是如何作为 non_blocking 工作的。

【问题讨论】:

我不知道libevent,但我怀疑你把non-blockingparallel 混在一起了 我开始使用 libevent 进行网络编程,因为它说您不需要线程,并且它与非阻塞套接字读取是异步的。但我无法执行。除此之外,我在网上没有找到任何用 libevent 编写非阻塞代码的好例子。 您可以使用循环和非阻塞套接字来获取异步服务器(性能很差),但是当您 sleep() 系统时 - 循环再次阻塞!你必须并行化! 【参考方案1】:

sleep(30) 您进行的调用正在阻塞,libevent 不会使用任何黑魔法来阻止 执行阻塞调用。 您需要小心并仅使用非阻塞 API。在这种情况下,您希望将响应延迟 30 秒,非常简单,您可以使用 libevent 的 evtimer_add() 。但是同样的原则适用于你想使用的任何类型的 api,你必须以非阻塞方式使用它们(文件读取、调用其他服务器、数据库访问等)。

【讨论】:

我对 evtime_add() 一无所知,但我也会对此进行锻炼。在这我们可以并行化事件吗?【参考方案2】:

感谢您的友好建议。

我使用线程概念做了非阻塞套接字,并按照以下方式并行化事件。

void *newThread(void *arg)
printf("Before thread start\n");
struct evhttp_request *req;
req = (struct evhttp_request *)arg;
sleep(30);
evhttp_send_reply(req, 200, "OK", NULL);
printf("After thread end\n");


static void dump_request_cb(struct evhttp_request *req, void *arg)

const char *cmdtype;
struct evkeyvalq *headers;
struct evkeyval *header;
struct evbuffer *buf;

printf("Request Start\n");
sleep(30); // delay to read request to check non blocking event.

switch (evhttp_request_get_command(req)) 
case EVHTTP_REQ_GET: cmdtype = "GET"; break;
case EVHTTP_REQ_POST: cmdtype = "POST"; break;
case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
default: cmdtype = "unknown"; break;


printf("Received a %s request for %s\nHeaders:\n",
    cmdtype, evhttp_request_get_uri(req));

headers = evhttp_request_get_input_headers(req);

for (header = headers->tqh_first; header;
   header = header->next.tqe_next) 
        printf("  %s: %s\n", header->key, header->value);

buf = evhttp_request_get_input_buffer(req);

puts("Input data: <<<");

while (evbuffer_get_length(buf)) 
        int n;
        char cbuf[128];
        n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
        if (n > 0)
                (void) fwrite(cbuf, 1, n, stdout);

puts(">>>");

这样,即使存在睡眠延迟,来自客户端的每个请求都会与之前的请求并行。 我在 libevent 模块中找不到任何解决方案,并使用替代线程概念完成了我的任务。

【讨论】:

以上是关于在 C 编程中使用 libevent 编写非阻塞事件的主要内容,如果未能解决你的问题,请参考以下文章

Protobuf 和非阻塞消息发送/接收

libevent到底是同步还是异步,是阻塞还是非阻塞

libevent到底是同步还是异步,是阻塞还是非阻塞

socket异步编程--libevent的使用

C 非阻塞键盘输入

socket的阻塞与非阻塞,同步与非同步