多路转接IO技术

 

 

select:

       #include <sys/select.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       //nfds:文件描述符个数,最大文件描述符加1, readfds、writefds、exceptfds

      //分别为要监听的读、写、异常事件。

      //timeout:在时间内监听事件

      //返回值:表示监听到的发生变化的描述符的个数

           struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

#include <sys/select.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>


int main(int argc, const char **argv)
{
    if (argc < 3)
    {
        perror("fail : ./a.out ip port");
        exit(1);
    }

    const char *ip = argv[1];
    int port = atoi(argv[2]);

    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server;
    bzero(&server, sizeof(server));

    //bind

    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    bind(lfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));  
    
    //listen
    int listenLen = 12;
    listen(lfd, listenLen);

    //select
    int maxfd = lfd;
    fd_set oldReadFd, newReadFd;
    FD_ZERO(&oldReadFd);
    FD_ZERO(&newReadFd);
    FD_SET(lfd, &oldReadFd);
    
    while(1){
        newReadFd = oldReadFd;
        int n = select(maxfd+1, &newReadFd, nullptr, nullptr, nullptr);
        if(FD_ISSET(lfd, &newReadFd)){
            struct sockaddr_in clientAddr;
            socklen_t socklen = sizeof(clientAddr);
            int newFd = accept(lfd, (struct sockaddr *)&clientAddr, &socklen);

            char clientIp[16] = "";
            inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, clientIp, sizeof(clientIp));
            std::cout << "new connection ip=" << clientIp << " port=" << ntohs(clientAddr.sin_port) << std::endl;

            FD_SET(newFd, &oldReadFd);

            if(newFd > maxfd){
                maxfd = newFd;
            }

            if(n == 1){
                continue ;
            }
        }

        for(size_t i = lfd+1; i <= maxfd; ++i){
            if(FD_ISSET(i, &newReadFd)){
                char buf[1500] = "";
                int ret = read(i, buf, sizeof(buf));
                if(ret < 0){
                    perror("read fail");
                    close(i);
                    FD_CLR(i, &oldReadFd);
                }else if(ret == 0){
                    std::cout << "close" << std::endl;
                    close(i);
                    FD_CLR(i, &oldReadFd);
                }else{
                    std::cout << "message: " << buf << std::endl;
                    write(i, buf, sizeof(buf));
                }
            }
        }
    }

    return 0;
}

 

poll:

       #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout); 

         struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <poll.h>
#include <unistd.h>


#define MAX_OPEN 128


int main(int argc, const char **argv)
{
    if(argc != 3){
        perror("input like this: ./a.out ip port");
        exit(0);
    }

    const  char *ip = argv[1];
    short port = atoi(argv[2]);

    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serverAddr;
    bzero(&serverAddr, sizeof(serverAddr));
    inet_pton(AF_INET, ip, &serverAddr.sin_addr.s_addr);
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = ntohs(port);
    
    //bind
    bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));

    //listen
    int listenLen = 128;
    listen(lfd, listenLen);


    struct pollfd clientFds[MAX_OPEN];
    clientFds[0].fd = lfd;
    clientFds[0].events = POLLIN;

    for(size_t i = 1; i < MAX_OPEN; ++i){
        clientFds[i].fd = -1;
    }

    int maxi = 0;

    struct sockaddr_in client;
    socklen_t socklen = sizeof(client);
    while(true){
        int nready = poll(clientFds, maxi+1, -1);

        if(clientFds[0].revents & POLLIN){
            int newFd = accept(clientFds[0].fd, (struct sockaddr *)&client, &socklen);
    
            char ip[16] = "";
            short port = ntohs(client.sin_port);
            inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip));
            std::cout << "new connection: ip=" << ip << " port=" << port << std::endl;

            size_t i;
            for(i = 1; i < MAX_OPEN; ++i){
                if(clientFds[i].fd == -1){
                    clientFds[i].fd = newFd;
                    clientFds[i].events = POLLIN;
                    break ;
                }
            }

            //can't handle it
            if(i == MAX_OPEN){
                perror("max connection number");
                exit(0);
            }

            if(maxi < i){
                maxi = i;
            }

            if(--nready == 0){
                continue ;
            }
        }    

        for(size_t i = 1; i <= maxi; ++i){
            if(clientFds[i].fd == -1){
                continue ;
            }

            if(clientFds[i].revents & POLLIN){
                char buf[1500] = "";

                int ret = read(clientFds[i].fd, buf, sizeof(buf));
                if(ret < 0){
                    perror("fail to recv");
                    exit(0);
                }else if(ret == 0){
                    close(clientFds[i].fd);
                    clientFds[i].fd = -1;
                    std::cout << "close" << std::endl;
                }else{
                    std::cout << "message: " << buf << std::endl;
                    write(clientFds[i].fd, buf, sizeof(buf));
                    if(--nready == 0){
                        break ;
                    }
                }
            }
        }                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
    }

    return 0;
}

epoll:

       int epoll_create(int size);   //size表示要监听的文件描述符的上限个数,自从2.6以后超过size会//自动扩容,往往填1。返回值:返回epoll的句柄,也就是epoll的文件描述符。

      int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  

      //在epfd指向的epoll中,对fd,event事件进行op操作,返回值:成功0,失败-1

      //op:EPOLL_CTL_ADD、EPOLL_CTL_ADD、EPOLL_CTL_ADD

           typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;      /* Epoll events */    //EPOLLIN、EPOLLIN等等
               epoll_data_t data;        /* User data variable */
           };

       int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

        //events, maxevents作为传出参数,返回值:监听到的文件描述符个数。      

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/epoll.h>
#include <poll.h>
#include <unistd.h>

#define MAXSIZE 128

int main(int argc, const char *argv[])
{
    if (argc != 3)
    {
        perror("input like this : ./a.out ip port");
        exit(0);
    }

    const char *ip = argv[1];
    short port = atoi(argv[2]);

    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serverAddr;
    bzero(&serverAddr, sizeof(serverAddr));
    inet_pton(AF_INET, ip, &serverAddr.sin_addr.s_addr);
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);

    bind(lfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    listen(lfd, 128);

    int epfd = epoll_create(1);
    struct epoll_event lfdEvent;
    lfdEvent.data.fd = lfd;
    lfdEvent.events = EPOLLIN;

    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &lfdEvent);

    struct epoll_event clientEpollEvent[MAXSIZE];
    while (1)
    {
        int ret = epoll_wait(epfd, clientEpollEvent, MAXSIZE, -1);
        if (ret < 0)
        {
            perror("epoll_wait wrong");
            exit(1);
        }
        else if (ret == 0)
        {
            continue;
        }
        else
        {
            for (size_t i = 0; i < ret; ++i)
            {
                if (clientEpollEvent[i].data.fd == lfd && clientEpollEvent[i].events & EPOLLIN)
                {
                    struct sockaddr_in client;
                    socklen_t clientLen;

                    int newFd = accept(lfd, (struct sockaddr *)&client, &clientLen);

                    struct epoll_event newEvent;
                    newEvent.data.fd = newFd;
                    newEvent.events = EPOLLIN;

                    epoll_ctl(epfd, EPOLL_CTL_ADD, newFd, &newEvent);
                    std::cout << "new connection " << newFd << std::endl;
                }
                else if(clientEpollEvent[i].events & EPOLLIN)
                {
                    int fd = clientEpollEvent[i].data.fd;
                    char buf[1024] = "";
                    int retNumber = read(fd, buf, sizeof(buf));
                    if (retNumber < 0)
                    {
                        perror("fail to read");
                        exit(0);
                    }
                    else if (retNumber == 0)
                    {
                        close(fd);
                        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &clientEpollEvent[i]);
                        std::cout << "close " << fd << std::endl;
                    }
                    else
                    {
                        std::cout << "message: " << buf << std::endl;
                    }
                }
            }
        }
    }

    return 0;
}

1.LT水平触发(默认):当缓存区的数据没有被一次性读取完,那么epoll_wait()函数会非阻塞的进行再次读取,直至读写缓存区的数据被读取完成。
2.ET边沿触发:每当进行一次读取操作后,epoll_wait()函数就会堵塞,直至下一次缓存区数据的写入,才会在此的触发读取操作。
区别:对于LT模式,保证了对数据的完整性读取,但是对一些不重要的不必要的数据来说,会加大内核对epoll_wait()函数的调用,加大开销。对于ET模式,可以在程序中设定读取的关键信息段,从而来减轻内核的开销,效率而言相比LT来说较高。但是值得注意的是读取缓存区需要及时将多余的信息清理掉,保证下一次读取的可靠性 ---需要1.配合while循环读取缓冲区。2.用fcntl设置非阻塞式读取缓冲区。3.用error的EAGAIN判断是否把数据读干净。4.注意跳出while循环。

epoll反应堆:epoll原理详解及epoll反应堆模型_~青萍之末~的博客-CSDN博客_epoll

//感觉是通过自定义结构体来在挂、删除红黑树上的节点。同时能通过回调函数进行特定的操作。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值