epoll 边沿触发 非阻塞 IO 服务器

在之前的文章中提到过Readn 函数:

ssize_t Readn(int fd, void *vptr, size_t n) 

试想这样一种情况:
1、server 循环使用 epoll_wait,监听 fd,fd 发生读事件,epoll_wait 通知 server。
2、server 接到通知,调用 Readn 函数,读取 500 字节。
3、但是,client 就发送了 200 字节,不足 500 字节。
4、此时,server 会阻塞在 Readn 函数上。
5、意味着 server 无法执行下一次 epoll_wait,意味着即使 client 再给 server 发数据,server 也得不到通知。
6、server 得不到通知,就无法读数据,就无法解除 Readn 函数的阻塞。
结果:造成“死锁”现象。
解决:将“套接字的文件描述符”设置为非阻塞 。

int flag = fcntl(cfd, F_GETFL); // 获得文件原属性
flag |= O_NONBLOCK; // 添加非阻塞属性
fcntl(connfd, F_SETFL, flag); // 使非阻塞属性生效

epoll的“边沿模式”下的“非阻塞读”模型

#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 8000
int main(void) 
{
    struct sockaddr_in servaddr;
    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)); // 绑定端口和IP
    listen(listenfd, 20); // 设置同时访问上限数
    struct epoll_event event; // epoll_ctl 传入参数
    struct epoll_event resevent[10]; // epoll_wait 传出参数
    int res, len;
    efd = epoll_create(10); // 创建epoll树根
    event.events = EPOLLIN | EPOLLET; /* ET 边沿触发,默认是水平触发 */
    printf("Accepting connections ...\n");
    truct sockaddr_in cliaddr;
    cliaddr_len = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); // 阻塞等待连接,获得connfd
    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为非阻塞读 */ // connfd 设置为非阻塞
    flag |= O_NONBLOCK;
    fcntl(connfd, F_SETFL, flag);
   event.data.fd = connfd; // 至此epoll_ctl 传入参数设置完毕
    epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); // 将connfd加入监听红黑树
    while (1) 
   {
        printf("epoll_wait begin\n");
        res = epoll_wait(efd, resevent, 10, -1); // 最多10个, 阻塞监听
        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;
}
// 如果是 非阻塞,边沿触发:read 不会阻塞,wait 只调用 1 次,while 循环 2 次
// 如果是 非阻塞,水平触发:read 不会阻塞,wait 需调用 2 次,while 循环 2 次
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值