• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            原文出處:http://www.wangafu.net/~nickm/libevent-book/01_intro.html
            轉自: http://blog.sina.com.cn/s/blog_56dee71a0100q2i9.html

            大多數程序員從阻塞IO調用開始學習。如果調用在操作完成之前,或者足夠的時間已經流逝使得網絡棧放棄操作之前,不會返回,那么就是異步的。比如說,在TCP連接上調用connect()時,操作系統將一個SYN分組排隊到TCP連接的另一端主機中。在收到來自對方主機的SYN ACK分組之前,或者直到足夠的時間已經流逝而決定放棄操作之前,控制不會返回到應用程序。

            這里有一個使用阻塞網絡調用的簡單客戶端示例。它打開到www.google.com的連接,發送一個簡單的HTTP請求,將響應打印到stdout。

            /* 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;
                    } 
            else if (result < 0) {
                        perror(
            "recv");
                        close(fd);
                        
            return 1;
                    }
                    fwrite(buf, 
            1, result, stdout);
                }

                close(fd);
                
            return 0;
            }

            上述代碼中的所有網絡調用都是阻塞的:在成功解析www.google.com,或者解析失敗之前,gethostbyname不會返回;連接建立之前connect不會返回;收到數據或者關閉之前recv調用不會返回;至少在清空輸出緩沖區到內核的寫緩沖區之前,send調用不會返回。

            這里,阻塞IO沒有什么不好的。如果沒有其他事情需要同時進行,阻塞IO會工作得很好。但是考慮需要同時處理多個連接的情形??紤]一個具體的例子:需要從兩個連接讀取輸入,但是不知道哪個連接將先收到輸入。程序可能是這樣的:

            /* This won't work. */
            char buf[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調用不會阻塞其他任何連接的進程。

            這里有另一個示例程序。它是一個簡單的服務器,在端口47013上監聽TCP連接,每次從其輸入緩沖區讀取一行,寫回其ROT13混淆結果。程序使用fork()調用為每個進入的連接創建一個新的進程。

            /* 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)
            {
                
            /* We don't want to use isalpha here; setting the locale would change
                 * which characters are considered alphabetical. 
            */
                
            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, 10);
                    
            if (result == 0) {
                        
            break;
                    } 
            else if (result == -1) {
                        perror(
            "read");
                        
            break;
                    }

                    
            /* We do this test to keep the user from 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;
            }

            是否有同時處理多個連接的完美解決方案?我可以停止編寫本書,去做其他事情嗎?不可以。首先,一些平臺上進程創建(甚至線程創建)的開銷是很大的?,F實中你可能想用線程池代替創建新進程。然而,線程的擴展性根本達不到期望。如果需要同時處理成千上萬個連接,處理上萬個線程的效率并不比在每個CPU上使用少量線程高。

            如果線程不是處理多個連接的答案,那么什么是呢?在Unix世界中,可以使用非阻塞套接字:

            fcntl(fd, F_SETFL, O_NONBLOCK);
                這里fd是套接字的文件描述符。將fd(套接字)設置為非阻塞之后,對fd進行網絡調用時,調用要么立即完成操作,要么返回一個特定的錯誤號,指示“現在不能進行操作,請重試”。這樣,示例程序可以寫作:
            /* This will work, but the performance will be unforgivably 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 have any data for us to read. */
                        
            else
                             handle_error(fd[i], errno);
                     } 
            else {
                        handle_input(fd[i], buf, n);
                     }
                }
            }

            使用非阻塞套接字,上述代碼可以工作,但只是在很少的情況下。程序性能將很糟糕,原因有兩個。首先,如果任何連接上都沒有數據可讀,循環還是會無限進行,消耗CPU時間。第二,如果用這種方式處理多于一兩個連接,程序將為每個連接進行內核調用,不論連接上是否有數據。我們需要的是一種可以告訴內核“等待這些套接字中的某一個有數據可讀,并且告知是哪一個”。

            對于此問題,現在仍然使用的最老的解決方案是select()。select()調用要求三個fd集合(作為位數組實現):一個用于讀取,一個用于寫入,一個用于異常。select()將等待集合中的某個套接字就緒,并且修改集合,使之僅包含已經就緒的套接字。

            這是使用select的相同示例:

            /* If you only have a couple dozen fds, this version won't be awful */
            fd_set readset;
            int i, n;
            char buf[1024];

            while (i_still_want_to_read()) {
                
            int maxfd = -1;
                FD_ZERO(
            &readset);

                
            /* Add all of the interesting fds to readset */
                
            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 still set 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't have any data for us to read. */
                            
            else
                                 handle_error(fd[i], errno);
                         } 
            else {
                            handle_input(fd[i], buf, n);
                         }
                    }
                }
            }
            這里是使用select重新實現的ROT13服務器:
            /* For sockaddr_in */
            #include 
            <netinet/in.h>
            /* For socket functions */
            #include 
            <sys/socket.h>
            /* For fcntl */
            #include 
            <fcntl.h>
            /* for select */
            #include 
            <sys/select.h>

            #include 
            <assert.h>
            #include 
            <unistd.h>
            #include 
            <string.h>
            #include 
            <stdlib.h>
            #include 
            <stdio.h>
            #include 
            <errno.h>

            #define MAX_LINE 16384

            char
            rot13_char(
            char c)
            {
                
            /* We don't want to use isalpha here; setting the locale would change
                 * which characters are considered alphabetical. 
            */
                
            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;
            }

            struct fd_state {
                
            char buffer[MAX_LINE];
                size_t buffer_used;

                
            int writing;
                size_t n_written;
                size_t write_upto;
            };

            struct fd_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(
            struct fd_state *state)
            {
                free(state);
            }

            void
            make_nonblocking(
            int fd)
            {
                fcntl(fd, F_SETFL, O_NONBLOCK);
            }

            int
            do_read(
            int fd, 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(
            int fd, 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);

            #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;
                }

                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, (struct sockaddr*)&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(
            int c, char **v)
            {
                setvbuf(stdout, NULL, _IONBF, 
            0);

                run();
                
            return 0;
            }

            事情還沒完。因為生成和讀取select位數組所需的時間與用于select的最大fd成比例,所以當套接字個數增加時,select調用的開銷將急劇增加。

            不同的操作系統為select提供了不同的替代功能,包括poll、epoll、kqueue、evports和/dev/poll。這些函數的性能都比select高,而且除了poll之外,添加、刪除套接字和通知套接字已經準備好IO的性能都是O(1)。

            不幸的是,這些接口都不是標準的。Linux有epoll、BSD(包括Darwin)有kqueue、Solaris有evports和/dev/poll……,然而沒有哪個操作系統有其他系統所擁有的調用。所以,如果想編寫可移植的高性能異步應用,就需要一個封裝所有這些接口的抽象,提供這些調用中性能最高的一個供使用。

            這就是Libevent API最底層所做的事情。Libevent為各種select替代提供了一致的接口,使用所運行在的計算機上的最高效版本。

            下面是另一個版本的異步ROT13服務器。這次用Libevent 2代替了select。注意fd_sets已經被拋棄:替代的是,將事件與結構體event_base關聯或者斷開關聯,這可能是用select、poll、epoll或者kqueue實現的。

            /* For sockaddr_in */
            #include 
            <netinet/in.h>
            /* For socket functions */
            #include 
            <sys/socket.h>
            /* For fcntl */
            #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_LINE 16384

            void do_read(evutil_socket_t fd, short events, void *arg);
            void do_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 considered alphabetical. 
            */
                
            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;
            }

            struct fd_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;
            };

            struct fd_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(
            struct fd_state *state)
            {
                event_free(state
            ->read_event);
                event_free(state
            ->write_event);
                free(state);
            }

            void
            do_read(evutil_socket_t fd, 
            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 evutil macro
                        return;
                    perror(
            "recv");
                    free_fd_state(state);
                }
            }

            void
            do_write(evutil_socket_t fd, 
            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 use evutil 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_t listener, 
            short eventvoid *arg)
            {
                
            struct event_base *base = arg;
                
            struct sockaddr_storage ss;
                socklen_t slen 
            = sizeof(ss);
                
            int fd = accept(listener, (struct sockaddr*)&ss, &slen);
                
            if (fd < 0) { // XXXX eagain??
                    perror("accept");
                } 
            else if (fd > FD_SETSIZE) {
                    close(fd); 
            // XXX replace all closes with 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);

            #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;
                }

                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(
            int c, char **v)
            {
                setvbuf(stdout, NULL, _IONBF, 
            0);

                run();
                
            return 0;
            }
            (代碼需要注意的其他地方:使用evutil_socket_t代替int來代表套接字;調用evutil_make_socket_nonblocking來將套接字設置為異步的,而不是調用fcntl(O_NONBLOCK)。這使得代碼兼容于Win32網絡API)

            使用是否便捷?(還有Windows呢?)

            你可能注意到代碼效率更高了,但是也更復雜了。使用fork的時候,(1)不需要為每個連接管理緩沖區:僅對每個進程使用一個單獨的在棧上分配的緩沖區。(2)不需要顯式跟蹤每個套接字是否在讀取或者寫入:這隱藏在代碼中了。(3)也不需要跟蹤每個操作是否完成的結構體:只需要循環和棧變量。

            此外,如果對Windows網絡有很深的體驗,你將認識到用于上述示例的時候,Libevent并不能取得優化的性能。在Windows上進行快速異步IO的方法不是使用select接口:而是使用IOCP。與其他快速網絡API不同的是,IOCP不是在套接字已經準備好某種操作時通知程序,然后程序可以進行相應的操作。替代的是,程序告知Windows網絡棧啟動某網絡操作,IOCP在操作完成時通知程序。

            幸運的是,Libevent 2 的“bufferevent”接口解決了所有這些問題:它提供了讓Libevent在Windows和Unix上都能夠有效實現的接口,讓程序編寫更簡單。

            這是最后一個版本的ROT13,使用bufferevent API:

            /* For sockaddr_in */
            #include 
            <netinet/in.h>
            /* For socket functions */
            #include 
            <sys/socket.h>
            /* For fcntl */
            #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>

            #define MAX_LINE 16384

            void do_read(evutil_socket_t fd, short events, void *arg);
            void do_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 considered alphabetical. 
            */
                
            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 is and 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 any clean up here */
                    
            /*  */
                } 
            else if (error & BEV_EVENT_ERROR) {
                    
            /* check errno to see what error occurred */
                    
            /*  */
                } 
            else if (error & BEV_EVENT_TIMEOUT) {
                    
            /* must be a timeout event handle, handle it */
                    
            /*  */
                }
                bufferevent_free(bev);
            }

            void
            do_accept(evutil_socket_t listener, 
            short eventvoid *arg)
            {
                
            struct event_base *base = arg;
                
            struct sockaddr_storage ss;
                socklen_t slen 
            = sizeof(ss);
                
            int fd = accept(listener, (struct sockaddr*)&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);

            #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;
                }

                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(
            int c, char **v)
            {
                setvbuf(stdout, NULL, _IONBF, 
            0);

                run();
                
            return 0;
            }

            這些真的很高效嗎?

            XXXX將在此寫有效的一節。libevent網頁上的benchmarks數據真的過期了。










             

            久久久久久国产精品无码下载| 久久亚洲精品成人无码网站| 狠狠精品久久久无码中文字幕| 97久久精品人人澡人人爽| 97精品伊人久久大香线蕉app| 久久综合久久综合久久| 91久久国产视频| 久久无码中文字幕东京热| 国产午夜免费高清久久影院| 国产精品永久久久久久久久久 | 亚洲精品乱码久久久久久按摩 | 亚洲色欲久久久综合网| aaa级精品久久久国产片| 精品免费久久久久国产一区| 性高湖久久久久久久久AAAAA| 97久久精品国产精品青草| 日韩欧美亚洲国产精品字幕久久久| 亚洲精品白浆高清久久久久久| 久久久久国产视频电影| 亚洲国产精品无码久久SM| 久久精品国产亚洲7777| 97久久精品无码一区二区| 亚洲精品午夜国产va久久| 精品乱码久久久久久久| 日日狠狠久久偷偷色综合96蜜桃 | 人人狠狠综合88综合久久| 日韩乱码人妻无码中文字幕久久| 久久精品中文字幕一区| 国产麻豆精品久久一二三| 热99RE久久精品这里都是精品免费 | 无码久久精品国产亚洲Av影片| 久久天天躁狠狠躁夜夜2020 | 日批日出水久久亚洲精品tv| 办公室久久精品| 亚洲国产精品久久久久婷婷软件 | 久久亚洲国产精品一区二区| 无码人妻精品一区二区三区久久| 久久亚洲AV无码精品色午夜| 亚洲伊人久久综合中文成人网| 久久无码AV中文出轨人妻| 国产精品一区二区久久精品无码|