天天看点

网络编程之IO复用机制(多路IO转接)之epoll的ET非阻塞092 总结

1 epoll的ET非阻塞案例

本篇也只是先简单描述epoll的ET非阻塞,真正的分析后面是详解epoll的ET非阻塞即epoll反应堆模型。

案例:同样与上一篇案例类似,为了方便测试,我们服务器不将lfd挂上树,只将cfd挂上树,使服务器只能接收一个客户端的连接(实际上一个以上也可以连接但是我们没有处理而已)。然后客户端写(客户端代码还是上一篇保持不变),服务端每次读5字节,但是由于服务器是ET模型,每次写只能触发一次epoll_wait返回,所以我在读的时候轮询读取,这样就能把管道剩余的数据读走,并且将描述符设为了非阻塞,所以read改成readn也不会出现问题。

注意:本篇(即包括本篇)和本篇之前epoll的代码全是用于验证,代码不能用于实际项目。

server.c

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>

#define MAXLINE 10
#define SERV_PORT 9000

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int efd, flag;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    listen(listenfd, 20);

    ///
    struct epoll_event event;
    struct epoll_event resevent[10];
    int res, len;

    efd = epoll_create(10);

    event.events = EPOLLIN | EPOLLET;     /* ET 边沿触发,默认是水平触发 */

    //event.events = EPOLLIN;
    printf("Accepting connections ...\n");
    cliaddr_len = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
    printf("received from %s at PORT %d\n",
            inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
            ntohs(cliaddr.sin_port));

    flag = fcntl(connfd, F_GETFL);          /* 修改connfd为非阻塞读 */
    flag |= O_NONBLOCK;
    fcntl(connfd, F_SETFL, flag);

    event.data.fd = connfd;
    epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);      //将connfd加入监听红黑树
    while (1) {
        printf("epoll_wait begin\n");
        res = epoll_wait(efd, resevent, 10, -1);        //最多10个, 阻塞监听,注意epoll的非阻塞与epoll_wait无关,非阻塞是指描述符cfd
        //printf("epoll_wait end res %d\n", res);

        if (resevent[0].data.fd == connfd) {
            while ((len = read(connfd, buf, MAXLINE/2)) >0 )    //非阻塞读, 轮询
                write(STDOUT_FILENO, buf, len);
        }
    }

    return 0;
}

           

1)结果可以看到,通过忙轮询读取套接字后,也能将套接字缓冲区剩余的数据读走。而不用每次等5秒读5个字节。

网络编程之IO复用机制(多路IO转接)之epoll的ET非阻塞092 总结

2 总结

1)好了,到此为止,我们将epoll的入门使用验证了个遍,真正关于epoll的ET非阻塞需要从反应堆开始看。但是这些入门的文章也需要看看,对理解epoll有很大的帮助。

继续阅读