一:异步IO简介

大多数的初级编程者都是从阻塞IO调用开始网络编程的。阻塞(同步)IO调用指的是:调用会一直阻塞,不会返回,直到发生下面两种情况之一。要么操作完成,要么经历相当长的时间,网络协议栈自己放弃。

比如,当在TCP连接上调用connect时,操作系统会发送SYN包到TCP的远端主机。connect会一直阻塞而不返回,直到它接收到了远端主机发来的SYN+ACK包,或者经历太长的时间而自己放弃。

下面是一个简单的使用阻塞网络调用的客户端例子。它链接google,发送简单的HTTP请求,然后将响应输出到stdout。

Example:A simple blocking HTTP client

/* For  sockaddr_in */

#include  <netinet/in.h>

/* For socket functions */

#include <sys/socket.h>

/* For gethostbyname */

#include <netdb.h>

#include <unistd.h>

#include <string.h>

#include <stdio.h>

int main(int c,char
**v)

{

const char query[] =

"GET / HTTP/1.0\r\n"

"Host: www.google.com\r\n"

"\r\n";

const char hostname[] = "www.google.com";

struct sockaddr_in sin;

struct hostent *h;

const char *cp;

int fd;

ssize_t n_written, remaining;

char buf[1024];

    /* Look up the IP address for the hostname.   Watch out; this isn't

       threadsafe on most platforms. */

h = gethostbyname(hostname);

if (!h) {

fprintf(stderr, "Couldn't lookup%s: %s", hostname, hstrerror(h_errno));

return 1;

}

if (h->h_addrtype != AF_INET) {

fprintf(stderr, "No ipv6 support,sorry.");

return 1;

}

    /* Allocate a new socket */

fd = socket(AF_INET, SOCK_STREAM, 0);

if (fd < 0) {

perror("socket");

return 1;

}

    /* Connect to the remote host. */

sin.sin_family = AF_INET;

sin.sin_port = htons(80);

sin.sin_addr = *(struct in_addr*)h->h_addr;

if (connect(fd, (struct
sockaddr*) &sin, sizeof(sin))) {

perror("connect");

close(fd);

return 1;

}

    /* Write the query. */

    /* XXX Can send succeed partially? */

cp = query;

remaining = strlen(query);

while (remaining) {

n_written = send(fd, cp, remaining, 0);

if (n_written <= 0) {

perror("send");

return 1;

}

remaining -= n_written;

cp += n_written;

}

    /* Get an answer back. */

while (1) {

ssize_t result = recv(fd, buf,sizeof(buf), 0);

if (result == 0) {

break;

}elseif (result < 0) {

perror("recv");

close(fd);

return 1;

}

fwrite(buf, 1, result, stdout);

}

close(fd);

return 0;

}

上面例子中,所有的网络调用都是阻塞的:gethostbyname直到成功或失败的解析了www.google.com才会返回;connect直到TCP建链成功了才会返回;recv直到收到数据时才会返回;send直到将输出flushed到内核的写缓冲区之后才会返回。

当然,阻塞IO并不总是无用的。如果应用程序在同一时刻不需要做其他事,那么阻塞IO同样会很好的工作。

但是,如果你要编写一个需要同时处理多个连接的程序,比如需要从两个连接中读取数据,而且不知道那个连接会先收到数据,那么下面就是一个不好的例子:

BadExample

/* This  won't work. */

charbuf[1024];

int i, n;

while(i_still_want_to_read()) {

for (i=0; i<n_sockets; ++i) {

n = recv(fd[i], buf, sizeof(buf), 0);

if (n==0)

handle_close(fd[i]);

else if (n<0)

handle_error(fd[i], errno);

else

handle_input(fd[i], buf, n);

}

}

如果fd[2]上首先有数据到来,但是上面的代码只有在fd[0]和fd[1]上接收到数据之后,才能去处理fd[2]上的数据。

有时,可以通过多线程(进程)来处理这样的问题。一个最简单的方式就是每个链接用一个线程(进程)进行处理。这样每个链接都会有自己的线程(进程)处理,一个链接上的阻塞IO调用就不会影响到其他链接上的处理。

下面就是一个例子:在TCP的40713端口上进行监听的ROT13服务器,每次从输入中接收一行数据,经过简单的处理后进行输出。它使用fork产生新的进程来处理每个链接。

Example:Forking ROT13 server

/* For sockaddr_in */

#include  <netinet/in.h>

/* For socket functions */

#include <sys/socket.h>

 

#include <unistd.h>

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

 

#define MAX_LINE 16384

 

char rot13_char(char c)

{

    if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))

        return c + 13;

    else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))

        return c - 13;

    else

        return c;

}

 

void child(int fd)

{

    char outbuf[MAX_LINE+1];

    size_t outbuf_used = 0;

    ssize_t result;

 

    while (1) 

    {

        char ch;

        result = recv(fd, &ch, 1, 0);

        if (result == 0) {

            break;

        } else if (result == -1) {

            perror("read");

            break;

        }

 

        /* We do this test to keep the userfrom overflowing the buffer. */

        if (outbuf_used < sizeof(outbuf)) {

            outbuf[outbuf_used++] = rot13_char(ch);

        }

 

        if (ch == '\n') {

            send(fd, outbuf, outbuf_used, 0);

            outbuf_used = 0;

            continue;

        }

    }

}

 

void run(void)

{

    int listener;

    struct sockaddr_in sin;

 

    sin.sin_family = AF_INET;

    sin.sin_addr.s_addr = 0;

    sin.sin_port = htons(40713);

 

    listener = socket(AF_INET, SOCK_STREAM, 0);

 

#ifndef WIN32

    {

        int one = 1;

        setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));

    }

#endif

 

    if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {

        perror("bind");

        return;

    }

 

    if (listen(listener, 16)<0) {

        perror("listen");

        return;

    }

 

 

 

    while (1) {

        struct sockaddr_storage ss;

        socklen_t slen = sizeof(ss);

        int fd = accept(listener, (struct sockaddr*)&ss, &slen);

        if (fd < 0) {

            perror("accept");

        } else {

            if (fork() == 0) {

                child(fd);

                exit(0);

            }

        }

    }

}

 

int main(int c, char **v)

{

    run();

    return 0;

}

这样,是否已经完美解决了同一时刻多连接的问题了呢?事实并非如此,第一,某些平台上,创建新进程(甚至是线程)是十分昂贵的。当然在实际环境中,可以使用线程池,而不是每次都创建新线程。第二,更重要的是,线程无法如你所愿的规模化使用。如果你的程序需要同时处理成千上万个链接的时候,处理成千上万个线程就不是那么高效的了。

    如果线程不是处理多连接的答案,那什么才是呢?

      在unix系统上,将socket设置为非阻塞:fcntl(fd, F_SETFL, O_NONBLOCK)。一旦将fd置为非阻塞,那么从此刻起,无论何时进行网络调用,该调用会立即返回,要么完成操作,返回成功,要么就是返回一个特定的错误码指出“当前无法完成任务,再试一次”。所以,上面2个链接的例子可以如下写:

BadExample: busy-polling all sockets

/* This will work, but the performance will beunforgivably bad. */

int i, n;

char buf[1024];

for (i=0;i < n_sockets; ++i)

fcntl(fd[i], F_SETFL, O_NONBLOCK);

while(i_still_want_to_read()) {

for (i=0; i < n_sockets; ++i) {

n = recv(fd[i], buf, sizeof(buf), 0);

if (n == 0) {

handle_close(fd[i]);

}else if (n < 0) {

if (errno == EAGAIN)

; /* The kernel didn't haveany data for us to read. */

else

handle_error(fd[i], errno);

} else {

handle_input(fd[i], buf, n);

}

}

}

上面就是使用非阻塞sockets的例子,它虽然可以工作,但是效率却很差,两个原因:第一,当每个链接都没有数据可读的时候,就会无限的轮训下去,用尽所有的CPU周期。第二,如果需要处理多个链接,那么不管是否有数据可读,每个链接都会进行一次内核调用。

所以,我们需要一种方法,可以告诉内核“一直等待,直到某个socket已经有准备好了,而且要告诉我那个socket准备好了”。

古老的解决方法是使用select,目前仍在使用。select使用三个socket fd集合(位数组):可读、可写和异常。它会一直等待,直到集合中的某一个socket已经准备好了,而且,select返回时,会更改集合,使其只包含那些已经准备好了的socket fd。使用select的例子如下:

Example:Using select

/* If youonly have a couple dozen fds, this version won't be awful */

fd_setreadset;

int i, n;

charbuf[1024];

while (i_still_want_to_read()){

int maxfd = -1;

FD_ZERO(&readset);

/* Add all of the interesting fds toreadset */

for (i=0; i < n_sockets; ++i) {

if (fd[i]>maxfd) maxfd = fd[i];

FD_SET(fd[i], &readset);

}

/*Wait until one or more fds are ready to read */

select(maxfd+1, &readset, NULL, NULL,NULL);

/* Process all of the fds that are stillset in readset */

for (i=0; i < n_sockets; ++i) {

if (FD_ISSET(fd[i], &readset)) {

n = recv(fd[i], buf, sizeof(buf),0);

if (n == 0) {

handle_close(fd[i]);

} else if (n < 0) {

if (errno == EAGAIN)

; /* The kernel didn'thave any data for us to read. */

else

handle_error(fd[i],errno);

} else {

handle_input(fd[i], buf, n);

}

}

}

}

一个完整的使用select的ROT13的服务器例子如下:

/* Forsockaddr_in */

#include<netinet/in.h>

/* For socketfunctions */

#include<sys/socket.h>

/* Forfcntl */

#include<fcntl.h>

/* forselect */

#include<sys/select.h>

#include<assert.h>

#include<unistd.h>

#include<string.h>

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#defineMAX_LINE 16384

char rot13_char(charc)

{

/* We don't want to use isalpha here;setting the locale would change

* which characters are consideredalphabetical. */

if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))

return c + 13;

else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))

return c - 13;

else

return c;

}

structfd_state {

char buffer[MAX_LINE];

size_t buffer_used;

int writing;

size_t n_written;

size_t write_upto;

};

structfd_state * alloc_fd_state(void)

{

struct fd_state *state =malloc(sizeof(struct fd_state));

if (!state)

return NULL;

state->buffer_used = state->n_written= state->writing =

state->write_upto = 0;

return state;

}

void free_fd_state(structfd_state *state)

{

free(state);

}

void make_nonblocking(intfd)

{

fcntl(fd, F_SETFL, O_NONBLOCK);

}

int do_read(intfd, struct fd_state *state)

{

char buf[1024];

int i;

ssize_t result;

while (1) {

result = recv(fd, buf, sizeof(buf), 0);

if (result <= 0)

break;

for (i=0; i < result; ++i)  {

if (state->buffer_used <sizeof(state->buffer))

state->buffer[state->buffer_used++] = rot13_char(buf[i]);

if (buf[i] == '\n') {

state->writing = 1;

state->write_upto =state->buffer_used;

}

}

}

if (result == 0) {

return 1;

} else if (result < 0) {

if (errno == EAGAIN)

return 0;

return -1;

}

return 0;

}

int do_write(intfd, struct fd_state *state)

{

while (state->n_written <state->write_upto) {

ssize_t result = send(fd,state->buffer + state->n_written,

state->write_upto - state->n_written, 0);

if (result < 0) {

if (errno == EAGAIN)

return 0;

return -1;

}

assert(result != 0);

state->n_written += result;

}

if (state->n_written ==state->buffer_used)

state->n_written =state->write_upto = state->buffer_used = 0;

state->writing = 0;

return 0;

}

void run(void)

{

int listener;

struct fd_state *state[FD_SETSIZE];

struct sockaddr_in sin;

int i, maxfd;

fd_set readset, writeset, exset;

sin.sin_family = AF_INET;

sin.sin_addr.s_addr = 0;

sin.sin_port = htons(40713);

for (i = 0; i < FD_SETSIZE; ++i)

state[i] = NULL;

listener = socket(AF_INET, SOCK_STREAM, 0);

make_nonblocking(listener);

#ifndefWIN32

{

int one = 1;

setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));

}

#endif

if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {

perror("bind");

return;

}

if (listen(listener, 16)<0) {

perror("listen");

return;

}

FD_ZERO(&readset);

FD_ZERO(&writeset);

FD_ZERO(&exset);

while (1) {

maxfd = listener;

FD_ZERO(&readset);

FD_ZERO(&writeset);

FD_ZERO(&exset);

FD_SET(listener, &readset);

for (i=0; i < FD_SETSIZE; ++i) {

if (state[i]) {

if (i > maxfd)

maxfd = i;

FD_SET(i, &readset);

if (state[i]->writing) {

FD_SET(i, &writeset);

}

}

}

if (select(maxfd+1, &readset,&writeset, &exset, NULL) < 0) {

perror("select");

return;

}

if (FD_ISSET(listener, &readset)) {

struct sockaddr_storage ss;

socklen_t slen = sizeof(ss);

int fd = accept(listener, (structsockaddr*)&ss, &slen);

if (fd < 0) {

perror("accept");

} else if (fd > FD_SETSIZE) {

close(fd);

} else {

make_nonblocking(fd);

state[fd] = alloc_fd_state();

assert(state[fd]);/*XXX*/

}

}

for (i=0; i < maxfd+1; ++i) {

int r = 0;

if (i == listener)

continue;

if (FD_ISSET(i, &readset)) {

r = do_read(i, state[i]);

}

if (r == 0 && FD_ISSET(i,&writeset)) {

r = do_write(i, state[i]);

}

if (r) {

free_fd_state(state[i]);

state[i] = NULL;

close(i);

}

}

}

}

int main(intc, char **v)

{

setvbuf(stdout, NULL, _IONBF, 0);

run();

return 0;

}

但是问题还没有解决。因为产生和读取select的位数组耗费的时间与最大的socket fd数成正比,所以当socket fd数变得很大时,select调用的性能就会下降很多。

不同的操作系统都提供了不同的select替代函数。包括poll, epoll, kqueue, evports和/dev/poll。所有这些接口都具有比select更好的性能,而且除了poll之外,他们在增加socket,删除socket,通知哪个socket准备好这些方面,都可以达到O(1)的性能。

不幸的是,所有这些不同的接口都没有形成标准。linux提供了epoll,BSDs提供了kqueue,Solaris提供了evports和/dev/poll,而且这些操作系统提供的接口相互独立。所以,当你需要编写一个可移植的、高性能异步应用时,你需要一个封装所有这些接口的抽象,而且提供那个最高效的接口。

这就是libeventAPI能提供的最底层的功能。它提供了一系列的select替代接口,并且使用当前操作系统所具有的,最高效的版本。

下面是另一个ROT13服务器的例子。该实例使用libevent2替代select。去除了fd_sets,而是使用event_base添加和删除事件,当然这是通过poll,epoll,kqueue等来实现的。

Example:A low-level ROT13 server with Libevent

/* Forsockaddr_in */

#include<netinet/in.h>

/* Forsocket functions */

#include<sys/socket.h>

/* Forfcntl */

#include<fcntl.h>

#include<event2/event.h>

#include<assert.h>

#include<unistd.h>

#include<string.h>

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#define MAX_LINE16384

voiddo_read(evutil_socket_t fd, short events, void *arg);

voiddo_write(evutil_socket_t fd, short events, void *arg);

char

rot13_char(charc)

{

/* We don't want to use isalpha here;setting the locale would change

* which characters are consideredalphabetical. */

if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))

return c + 13;

else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))

return c - 13;

else

return c;

}

structfd_state {

char buffer[MAX_LINE];

size_t buffer_used;

size_t n_written;

size_t write_upto;

struct event *read_event;

struct event *write_event;

};

structfd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd)

{

struct fd_state *state =malloc(sizeof(struct fd_state));

if (!state)

return NULL;

state->read_event = event_new(base, fd,EV_READ|EV_PERSIST, do_read, state);

if (!state->read_event) {

free(state);

return NULL;

}

state->write_event = event_new(base, fd,EV_WRITE|EV_PERSIST, do_write, state);

if (!state->write_event) {

event_free(state->read_event);

free(state);

return NULL;

}

state->buffer_used = state->n_written= state->write_upto = 0;

assert(state->write_event);

return state;

}

void free_fd_state(structfd_state *state)

{

event_free(state->read_event);

event_free(state->write_event);

free(state);

}

void do_read(evutil_socket_tfd, short events, void *arg)

{

struct fd_state *state = arg;

char buf[1024];

int i;

ssize_t result;

while (1) {

assert(state->write_event);

result = recv(fd, buf, sizeof(buf), 0);

if (result <= 0)

break;

for (i=0; i < result; ++i)  {

if (state->buffer_used <sizeof(state->buffer))

state->buffer[state->buffer_used++] = rot13_char(buf[i]);

if (buf[i] == '\n') {

assert(state->write_event);

event_add(state->write_event,NULL);

state->write_upto =state->buffer_used;

}

}

}

if (result == 0) {

free_fd_state(state);

} else if (result < 0) {

if (errno == EAGAIN) // XXXX use evutilmacro

return;

perror("recv");

free_fd_state(state);

}

}

void do_write(evutil_socket_tfd, short events, void *arg)

{

struct fd_state *state = arg;

while (state->n_written <state->write_upto) {

ssize_t result = send(fd,state->buffer + state->n_written,

state->write_upto - state->n_written, 0);

if (result < 0) {

if (errno == EAGAIN) // XXX useevutil macro

return;

free_fd_state(state);

return;

}

assert(result != 0);

state->n_written += result;

}

if (state->n_written ==state->buffer_used)

state->n_written =state->write_upto = state->buffer_used = 1;

event_del(state->write_event);

}

void do_accept(evutil_socket_tlistener, short event, void *arg)

{

struct event_base *base = arg;

struct sockaddr_storage ss;

socklen_t slen = sizeof(ss);

int fd = accept(listener, (structsockaddr*)&ss, &slen);

if (fd < 0) { // XXXX eagain??

perror("accept");

} else if (fd > FD_SETSIZE) {

close(fd); // XXX replace all closeswith EVUTIL_CLOSESOCKET */

} else {

struct fd_state *state;

evutil_make_socket_nonblocking(fd);

state = alloc_fd_state(base, fd);

assert(state); /*XXX err*/

assert(state->write_event);

event_add(state->read_event, NULL);

}

}

void run(void)

{

evutil_socket_t listener;

struct sockaddr_in sin;

struct event_base *base;

struct event *listener_event;

base = event_base_new();

if (!base)

return; /*XXXerr*/

sin.sin_family = AF_INET;

sin.sin_addr.s_addr = 0;

sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);

evutil_make_socket_nonblocking(listener);

#ifndefWIN32

{

int one = 1;

setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));

}

#endif

if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {

perror("bind");

return;

}

if (listen(listener, 16)<0) {

perror("listen");

return;

}

listener_event = event_new(base, listener,EV_READ|EV_PERSIST, do_accept, (void*)base);

/*XXX check it */

event_add(listener_event, NULL);

event_base_dispatch(base);

}

int main(intc, char **v)

{

setvbuf(stdout, NULL, _IONBF, 0);

run();

return 0;

}

(上面的代码需要注意的是,使用evutil_socket_t,而不是int作为socket的类型;使用evutil_make_socket_nonblocking而不是fcntl(O_NONBLOCK),将socket转为非阻塞。这些改变使得我们的代码可以兼容win32平台下的网络API。)
 
二:更方便并且兼容windows
    我们的代码虽然更加高效了,但是也变得更加复杂了。回到我们使用fork的例子,我们没有为每个链接都管理一个缓存,我们只是在每个进程上使用了独立的栈缓存。实际上,我们无需明确的跟踪哪个socket在读或写,在代码中它是隐含的。我们也无需一个跟踪多少操作已经完成的结构体,我们可以仅仅使用循环和栈变量即可。
    另外,如果你对windows上的网络编程很熟悉,则可以看出,使用libevent的上面的例子没有达到最佳的性能。在Windows上,高效的异步IO与并不是类似于select那样的机制,而是使用IOCP(IO Completion Ports)API。与其他高效网络API不同的是,IOCP并不通知你的程序哪个socket已经准备好操作了,相反的,程序告诉windows网络栈开始一个网络操作,而IOCP告诉程序操作已经完成了。
 
    幸运的是,libevent2的“bufferevents”接口可以解决上面的问题:它使得程序编写更加简单,而且可以在windows上、unix上都提供最高效的接口。下面是最后一个ROT13服务器的例子,它使用了bufferevents API:

Example:A simpler ROT13 server with Libevent

/* Forsockaddr_in */

#include<netinet/in.h>

/* Forsocket functions */

#include<sys/socket.h>

/* Forfcntl */

#include<fcntl.h>

#include<event2/event.h>

#include<event2/buffer.h>

#include<event2/bufferevent.h>

#include<assert.h>

#include<unistd.h>

#include<string.h>

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#defineMAX_LINE 16384

void do_read(evutil_socket_tfd, short events, void *arg);

voiddo_write(evutil_socket_t fd, short events, void *arg);

char  rot13_char(char c)

{

/* We don't want to use isalpha here;setting the locale would change

* which characters are consideredalphabetical. */

if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))

return c + 13;

else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))

return c - 13;

else

return c;

}

void  readcb(struct bufferevent *bev, void *ctx)

{

struct evbuffer *input, *output;

char *line;

size_t n;

int i;

input = bufferevent_get_input(bev);

output = bufferevent_get_output(bev);

while ((line = evbuffer_readln(input, &n,EVBUFFER_EOL_LF))) {

for (i = 0; i < n; ++i)

line[i] = rot13_char(line[i]);

evbuffer_add(output, line, n);

evbuffer_add(output, "\n",1);

free(line);

}

if (evbuffer_get_length(input) >=MAX_LINE) {

/* Too long; just process what there isand go on so that the buffer

* doesn't grow infinitely long. */

char buf[1024];

while (evbuffer_get_length(input)) {

int n = evbuffer_remove(input, buf,sizeof(buf));

for (i = 0; i < n; ++i)

buf[i] = rot13_char(buf[i]);

evbuffer_add(output, buf, n);

}

evbuffer_add(output, "\n",1);

}

}

void  errorcb(struct bufferevent *bev, short error,void *ctx)

{

if (error & BEV_EVENT_EOF) {

/* connection has been closed, do anyclean up here */

/* ... */

} else if (error & BEV_EVENT_ERROR) {

/* check errno to see what erroroccurred */

/* ... */

} else if (error & BEV_EVENT_TIMEOUT) {

/* must be a timeout event handle,handle it */

/* ... */

}

bufferevent_free(bev);

}

void  do_accept(evutil_socket_t listener, shortevent, void *arg)

{

struct event_base *base = arg;

struct sockaddr_storage ss;

socklen_t slen = sizeof(ss);

int fd = accept(listener, (structsockaddr*)&ss, &slen);

if (fd < 0) {

perror("accept");

} else if (fd > FD_SETSIZE) {

close(fd);

} else {

struct bufferevent *bev;

evutil_make_socket_nonblocking(fd);

bev = bufferevent_socket_new(base, fd,BEV_OPT_CLOSE_ON_FREE);

bufferevent_setcb(bev, readcb, NULL,errorcb, NULL);

bufferevent_setwatermark(bev, EV_READ,0, MAX_LINE);

bufferevent_enable(bev,EV_READ|EV_WRITE);

}

}

void run(void)

{

evutil_socket_t listener;

struct sockaddr_in sin;

struct event_base *base;

struct event *listener_event;

base = event_base_new();

if (!base)

return; /*XXXerr*/

sin.sin_family = AF_INET;

sin.sin_addr.s_addr = 0;

sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);

evutil_make_socket_nonblocking(listener);

#ifndefWIN32

{

int one = 1;

setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));

}

#endif

if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {

perror("bind");

return;

}

if (listen(listener, 16)<0) {

perror("listen");

return;

}

listener_event = event_new(base, listener,EV_READ|EV_PERSIST, do_accept, (void*)base);

/*XXX check it */

event_add(listener_event, NULL);

event_base_dispatch(base);

}

int main(intc, char **v)

{

setvbuf(stdout, NULL, _IONBF, 0);

run();

return 0;

}

原文:http://www.wangafu.net/~nickm/libevent-book/01_intro.html

Libevent:0异步IO简介的更多相关文章

  1. 异步IO简介

    最近想学习一下libevent,就先翻译一下libevent的官方文档吧. 英文原文链接:http://www.wangafu.net/~nickm/libevent-book/01_intro.ht ...

  2. 以寡治众各个击破,超大文件分片上传之构建基于Vue.js3.0+Ant-desgin+Tornado6纯异步IO高效写入服务

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_218 分治算法是一种很古老但很务实的方法.本意即使将一个较大的整体打碎分成小的局部,这样每个小的局部都不足以对抗大的整体.战国时期 ...

  3. Python 的异步 IO:Asyncio 简介

    转载自https://segmentfault.com/a/1190000008814676 好文章 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时, ...

  4. 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  5. 转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    此文章为转载,如有侵权,请联系本人.转载出处,http://blog.chinaunix.net/uid-28458801-id-4464639.html 同步(synchronous) IO和异步( ...

  6. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  7. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  8. Day11-协程/异步IO/RabbitMQ

    协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候 ...

  9. Python之路第一课Day10--随堂笔记(异步IO\数据库\队列\缓存)

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

随机推荐

  1. 评价目标检测(object detection)模型的参数:IOU,AP,mAP

    首先我们为什么要使用这些呢? 举个简单的例子,假设我们图像里面只有1个目标,但是定位出来10个框,1个正确的,9个错误的,那么你要按(识别出来的正确的目标/总的正确目标)来算,正确率100%,但是其实 ...

  2. RMQ—ST表

    RMQ(Range Minimum/Maximum Query),RMQ是一个求给定范围内最大最小值的问题.我们一般使用st算法来解决这类问题(Sparse Table).这个算法原理不难,主要是各种 ...

  3. 使用Python的requests库作接口测试——请求对象与响应对象

    任何时候调用requests.*()操作接口时,我们都在做两件事情: 1.构建一个Request对象,该对象被发送到服务器去请求或查询一些资源: 2.一旦requests得到一个从服务器返回的响应,就 ...

  4. [Array]283. Move Zeroes

    Given an array nums, write a function to move all 0's to the end of it while maintaining the relativ ...

  5. 斐波那契字符串_KMP

    前言:通过这道题恶补了一下字符串匹配的知识 思路:首先就是求出菲波那切字符串,这个很简单,但是要注意递归超时的问题,可以考虑加上备忘录,或者用递推法,接下来就是匹配问题了,常规的BF会超时,所以要用K ...

  6. TZ_13_Hystix的熔断器

    1.作用:当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果. 熔断器的三种状态: Closed:关闭状态(断路器关闭),所有请求都正 ...

  7. WPF 的另类资源方式 Resources.resx

      类似Winform的搞法,可以把资源放到Resources.resx中. 1.字符串 打开这个编辑器后,输入Name和Value就可以了. CS代码里面,很简单的调用: var title = W ...

  8. HTTP_REFERER的用法及伪造

    引言 在php中,可以使用$_SERVER[‘HTTP_REFERER’]来获取HTTP_REFERER信息,关于HTTP_REFERER,php文档中的描述如下: “引导用户代理到当前页的前一页的地 ...

  9. Spring MVC 搭建web项目示例

    环境为Eclipse 1:新建Dynamic web project  : springMvcDemo 2:下载spring的jar包,把jar包复制到WEB-INF/lib目录下 3.添加配置文件w ...

  10. 2019-9-2-windows-10「设置」应用完整ms-settings快捷方式汇总

    title author date CreateTime categories windows-10「设置」应用完整ms-settings快捷方式汇总 lindexi 2019-09-02 12:57 ...