Linux非阻塞IO(七)使用epoll重新实现客户端

使用poll与epoll的区别主要在于:

poll可以每次重新装填fd数组,但是epoll的fd是一开始就加入了,不可能每次都重新加入

于是采用这种策略:

epoll除了listenfd一开始就监听read事件,其他的客户fd加入epoll时,监听的事件都为空。

然后在每次epoll_wait之前,使用epoll_ctl重新设置fd的监听事件

所以这部分的代码如下:

 

//重新装填epoll事件
        sockfd_event = 0;
        stdin_event = 0;
        stdout_event = 0;
        //epoll无法每次都重新装填,所以给每个fd添加一个空事件
        
        if(buffer_is_readable(&sendbuf))
        {
            sockfd_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&sendbuf))
        {
            stdin_event |= kReadEvent;
        }
        if(buffer_is_readable(&recvbuf))
        {
            stdout_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&recvbuf))
        {
            sockfd_event |= kReadEvent;
        }

        epoll_mod_fd(epollfd, sockfd, sockfd_event);
        epoll_mod_fd(epollfd, STDIN_FILENO, stdin_event);
        epoll_mod_fd(epollfd, STDOUT_FILENO, stdout_event);

理解了这部分代码,整理部分与poll基本一致:

#define _GNU_SOURCE
#include "sysutil.h"
#include "buffer.h"
#include <sys/epoll.h>

int main(int argc, char const *argv[])
{
    //创建client套接字
    int sockfd = tcp_client(0);
    //调用非阻塞connect函数
    int ret = nonblocking_connect(sockfd, "localhost", 9981, 5000);
    if(ret == -1)
    {
        perror("Connect Timeout .");
        exit(EXIT_FAILURE);
    }

    //将三个fd设置为Non-Blocking
    activate_nonblock(sockfd);
    activate_nonblock(STDIN_FILENO);
    activate_nonblock(STDOUT_FILENO);


    buffer_t recvbuf; //sockfd -> Buffer -> stdout
    buffer_t sendbuf; //stdin -> Buffer -> sockfd

    //初始化缓冲区
    buffer_init(&recvbuf);
    buffer_init(&sendbuf);


    //创建epoll
    int epollfd = epoll_create1(0);
    if(epollfd == -1)
        ERR_EXIT("create epoll");
    struct epoll_event events[1024];

    uint32_t sockfd_event = 0;
    uint32_t stdin_event = 0;
    uint32_t stdout_event = 0;

    epoll_add_fd(epollfd, sockfd, sockfd_event);
    epoll_add_fd(epollfd, STDIN_FILENO, stdin_event);
    epoll_add_fd(epollfd, STDOUT_FILENO, stdout_event);


    while(1)
    {
        //重新装填epoll事件
        sockfd_event = 0;
        stdin_event = 0;
        stdout_event = 0;
        //epoll无法每次都重新装填,所以给每个fd添加一个空事件
        
        if(buffer_is_readable(&sendbuf))
        {
            sockfd_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&sendbuf))
        {
            stdin_event |= kReadEvent;
        }
        if(buffer_is_readable(&recvbuf))
        {
            stdout_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&recvbuf))
        {
            sockfd_event |= kReadEvent;
        }

        epoll_mod_fd(epollfd, sockfd, sockfd_event);
        epoll_mod_fd(epollfd, STDIN_FILENO, stdin_event);
        epoll_mod_fd(epollfd, STDOUT_FILENO, stdout_event);


        //监听fd数组
        int nready = epoll_wait(epollfd, events, 1024, 5000);
        if(nready == -1)
            ERR_EXIT("epoll wait");
        else if(nready == 0)
        {
            printf("epoll timeout.\n");
            continue;
        }
        else
        {
            int i;
            for(i = 0; i < nready; ++i)
            {
                int peerfd = events[i].data.fd;
                int revents = events[i].events;
                if(peerfd == sockfd && revents & kReadREvent)
                {
                    //从sockfd接收数据到recvbuf
                    if(buffer_read(&recvbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "server close.\n");
                        exit(EXIT_SUCCESS);
                    } 
                }
                    
                if(peerfd == sockfd && revents & kWriteREvent)
                {
                    buffer_write(&sendbuf, peerfd); //将sendbuf中的数据写入sockfd
                }

                if(peerfd == STDIN_FILENO && revents & kReadREvent)
                {
                    //从stdin接收数据写入sendbuf
                    if(buffer_read(&sendbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "exit.\n");
                        exit(EXIT_SUCCESS);
                    } 
                }

                if(peerfd == STDOUT_FILENO && revents & kWriteREvent)
                {
                    buffer_write(&recvbuf, peerfd); //将recvbuf中的数据输出至stdout
                }
            }
        }

    }

}

转载于:https://www.cnblogs.com/inevermore/p/4055057.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个使用epoll实现的简单的Linux网络编程例子: ```c #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/epoll.h> #define MAX_EVENTS 1024 #define PORT 12345 int setnonblocking(int sockfd) { int flags = fcntl(sockfd, F_GETFL, 0); if (flags == -1) { return -1; } flags |= O_NONBLOCK; if (fcntl(sockfd, F_SETFL, flags) == -1) { return -1; } return 0; } int main(int argc, char *argv[]) { int listenfd, connfd, nfds, epollfd; ssize_t n; char buf[1024]; struct sockaddr_in servaddr, cliaddr; struct epoll_event ev, events[MAX_EVENTS]; // 创建监听socket,绑定地址和端口 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(PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 5); // 创建epoll epollfd = epoll_create1(0); if (epollfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } // 设置监听socket为非阻塞 if (setnonblocking(listenfd) < 0) { perror("setnonblocking"); exit(EXIT_FAILURE); } // 添加监听socket事件到epoll ev.events = EPOLLIN | EPOLLET; ev.data.fd = listenfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) { perror("epoll_ctl: listenfd"); exit(EXIT_FAILURE); } // 循环处理epoll事件 for (;;) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } for (int i = 0; i < nfds; i++) { if (events[i].data.fd == listenfd) { // 新连接 socklen_t clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); if (connfd == -1) { perror("accept"); exit(EXIT_FAILURE); } // 设置连接socket为非阻塞 if (setnonblocking(connfd) < 0) { perror("setnonblocking"); exit(EXIT_FAILURE); } // 添加连接socket事件到epoll ev.events = EPOLLIN | EPOLLOUT | EPOLLET; ev.data.fd = connfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) { perror("epoll_ctl: connfd"); exit(EXIT_FAILURE); } // 打印新连接的客户端地址 printf("Accepted connection from %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); } else { // 已连接 if (events[i].events & EPOLLIN) { // 可读 while ((n = read(events[i].data.fd, buf, sizeof(buf))) > 0) { // 处理读取到的数据 // ... } if (n == 0) { // 对方关闭连接 close(events[i].data.fd); printf("Closed connection on descriptor %d\n", events[i].data.fd); } else if (errno != EAGAIN && errno != EINTR) { // 发生错误 perror("read error"); close(events[i].data.fd); } } else if (events[i].events & EPOLLOUT) { // 可写 // 发送数据到连接socket // ... } else { // 其他事件 printf("Unknown event: %d\n", events[i].events); } } } } // 关闭监听socket和epoll close(listenfd); close(epollfd); return 0; } ``` 这个例子使用epoll监听TCP连接事件,支持EPOLLET边缘触发模式和非阻塞IO。当有新连接到来时,将连接socket添加到epoll中,并打印客户端地址;当连接socket可读或可写时,分别处理读取和发送数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值